优雅的处理 Rust 错误

  1. unwarp
fn main() {
	let path = "/home/test.txt";
	read_file(path);
}

fn read_file(path: &str) -> String {
	// unwarp 用于 Option 或 Result的结果获取
	std::fs::read_to_string(path).unwrap()
}

上面程序会 panic

避免使用 unwrap

fn main() {
	let path = "/home/test.txt";
	match read_file(path) {
		Ok(file) => {
			// 可能有嵌套, 再一次 match
			println!("{}", file);
		}
		Err(e) => {
			println!("{}, {}", path, e);
		}
	}
}
//枚举的好处: 多选一, OK 和 Err(限定在有限的集合中)
//健壮性: 对程序运行的结果全部可控
fn read_file(path: &str) -> Result<String, std::io::Error>{
	std::fs::read_to_string(path)
}

unwrap -> result,mathch(不会 painc) -> 自定义错误 + From 转换(?)

自定义错误

// /home/opc/.cargo/bin/cargo run --package cider --bin cider 

use std::error::Error;


#[derive(Debug)]
struct ChildError;

#[derive(Debug)]
struct CustomError {
	err: ChildError
}

impl std::fmt::Display for CustomError {

    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
	    write!(f, "Custom error is here")
    }
}

impl std::error::Error for CustomError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        // None
	    Some(&self.err)
    }
}


impl std::fmt::Display for ChildError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
	    write!(f, "Child error is here")
    }
}

impl std::error::Error for ChildError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

fn get_supper_error() -> Result<(), CustomError> {
	Err(CustomError{ err: ChildError})
}

fn main() {
    match get_supper_error() {
        Err(err) => {
            println!("{}",err); 
            println!("caused by {}", err.source().unwrap());
        }
        _ => {
            println!("No error");
        }
    } 
}	

用 Option 来包装

嵌套解决方案

// /home/opc/.cargo/bin/cargo run --package cider --bin cider 

fn read_file(path: &str) -> Result<String, std::io::Error> {
    std::fs::read_to_string(path)
}

fn to_utf8(v: &[u8]) -> Result<&str, std::str::Utf8Error> {
    std::str::from_utf8(v)
}

fn to_u32(v: &str) -> Result<u32, std::num::ParseIntError> {
    v.parse::<u32>()
}

fn main() {
    let path = "/home/opc/workspace/develop/languages/rust/cider/test.txt";
    match read_file(path) {
        Ok(v) => {
            match to_utf8(v.as_bytes()) {
               Ok(u) => {
                match to_u32(u)      {
                     Ok(t) => {
                         println!("{}", t)
                     },
                     Err(_) => {}
                }

               }
               Err(_) => {}
            }
        }
        Err(_) => {}
    }
}

上面这种方案嵌套层次太多了

解决方案 From(转化) + ?

// /home/opc/.cargo/bin/cargo run --package cider --bin cider 

use std::{fmt::Display, num::ParseIntError, str::Utf8Error, error::Error};


#[derive(Debug)]
enum CustomError {
    ParseIntError(std::num::ParseIntError),
    Utf8Error(std::str::Utf8Error),
    IoError(std::io::Error),
}

impl Error for CustomError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match &self {
            CustomError::IoError(ref e) => Some(e),
            CustomError::Utf8Error(ref e) => Some(e),
            CustomError::ParseIntError(ref e) => Some(e),
        }
    }
}

impl Display for CustomError {

    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match &self {
            CustomError::IoError(ref e) => e.fmt(f),
            CustomError::Utf8Error(ref e) => e.fmt(f),
            CustomError::ParseIntError(ref e) => e.fmt(f)
        }
        
    }
}

impl From<ParseIntError> for CustomError {
    fn from(s: ParseIntError) -> Self {
        CustomError::ParseIntError(s)
    }
}

impl From<Utf8Error> for CustomError {
    fn from(s: Utf8Error) -> Self {
        CustomError::Utf8Error(s)
    }
}

impl From<std::io::Error> for CustomError {
    fn from(s: std::io::Error) -> Self {
        CustomError::IoError(s)
    }
}

fn read_file(path: &str) -> Result<String, std::io::Error> {
    std::fs::read_to_string(path)
}

fn to_utf8(v: &[u8]) -> Result<&str, std::str::Utf8Error> {
    std::str::from_utf8(v)
}

fn to_u32(v: &str) -> Result<u32, std::num::ParseIntError> {
    v.parse::<u32>()
}

fn main() -> Result<(), CustomError> {
    let path = "/home/opc/workspace/develop/languages/rust/cider/test.txt";
    let content = read_file(path)?;
    let u = to_utf8(content.as_bytes())?;
    let v = to_u32(u)?; 
    println!("{}", v);
    Ok(())
}