rust-error-handle

detail rust error handle

MIT License

Stars
70
Committers
2

Rust

:https://github.com/baoyachi/rust-error-handle

1.

15-20minRust``Rust


Rust

2.

Rust``Rust******unwrap()******panicRust

Rust``Rust

3. unwrap!

,

fn main() {
    let path = "/tmp/dat";
    println!("{}", read_file(path));
}

fn read_file(path: &str) -> String {
    std::fs::read_to_string(path).unwrap()
}
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/libcore/result.rs:1188:5
stack backtrace:
   0: backtrace::backtrace::libunwind::trace
             at /Users/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.40/src/backtrace/libunwind.rs:88
  ...
  15: rust_sugar::read_file
             at src/main.rs:7
  16: rust_sugar::main
             at src/main.rs:3
  ...
  25: rust_sugar::read_file
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

path

unwrap() rustOption``Result unwrap() !

****bugpanic

**unwrap()**code review,

unwrap() unwrap()

demoDemo****, unwrap()

unit test****mock unwrap() unwrap()

4.

unwrap()rustunwrap() Rust

4.1 golang

golang

package main

import (
    "io/ioutil"
    "log"
)

func main() {
    path := "/tmp/dat"  //
    file, err := readFile(path) 
    if err != nil {
        log.Fatal(err) //
    }
    println("%s", file) //
}

func readFile(path string) (string, error) {
    dat, err := ioutil.ReadFile(path)  //
    if err != nil {  //errnil
        return "", err  //nil,err
    }
    return string(dat), nil  //err=nil,
}
2020/02/24 01:24:04 open /tmp/dat: no such file or directory

golang err!=nil golang``if err!=nil``if err!=nil

4.2 Rust

golang``Rust

fn main() {
    let path = "/tmp/dat";  //
    match read_file(path) { //
        Ok(file) => { println!("{}", file) } //OK 
        Err(e) => { println!("{} {}", path, e) } //Err
    }
}

fn read_file(path: &str) -> Result<String,std::io::Error> { //Result
    std::fs::read_to_string(path) //
}
No such file or directory (os error 2)

Rust``Result``enum,

pub enum Result<T, E> {
    /// Contains the success value
    Ok(#[stable(feature = "rust1", since = "1.0.0")] T),

    /// Contains the error value
    Err(#[stable(feature = "rust1", since = "1.0.0")] E),
}

Result``Result``match``ResultOk(Err)

golang``Rust

  • golangfileerr``nil,return**** if err!=nil
  • Rust``Result``Result``Ok``Err``Result``Ok,Err``return: Ok or Err
  • golang if err!=nil **** lint bug
  • Rust``match,

match``match

5. Rust

golang``Rust``returnRust

Rust``std::error::Errortrait https://doc.rust-lang.org/std/error/trait.Error.html

pub trait Error: Debug + Display {

    fn description(&self) -> &str {
        "description() is deprecated; use Display"
    }

    #[rustc_deprecated(since = "1.33.0", reason = "replaced by Error::source, which can support \
                                                   downcasting")]

    fn cause(&self) -> Option<&dyn Error> {
        self.source()
    }

    fn source(&self) -> Option<&(dyn Error + 'static)> { None }

    #[doc(hidden)]
    fn type_id(&self, _: private::Internal) -> TypeId where Self: 'static {
        TypeId::of::<Self>()
    }

    #[unstable(feature = "backtrace", issue = "53487")]
    fn backtrace(&self) -> Option<&Backtrace> {
        None
    }
}
  • description()``impl Display impl, to_string()

  • cause()1.33.0source()``impl

  • source()``Err``Some(e),None

    • Error``Error,Error,None``None****
    • ErrorError,ErrorSome(err),****
  • type_id()

  • backtrace()``unstable``Rust``stable

  • Errorimpl std::fmt::Debugtrait,#[derive(Debug)]

error

  • impl std::fmt::Displaytrait,**** fmt(...)
  • impl std::fmt::Debug``trait``#[derive(Debug)]
  • impl std::error::Error``trait,error****std::error::Error``source()

Rust:CustomError

use std::error::Error;

/// Error,std::fmt::Debugtrait
#[derive(Debug)]
struct CustomError {
    err: ChildError,
}

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

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


/// Error,std::fmt::Debugtrait
#[derive(Debug)]
struct ChildError;

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

///Errortrait,Error,source()
impl std::error::Error for ChildError {}

///Resulterror:CustomError
fn get_super_error() -> Result<(), CustomError> {
    Err(CustomError { err: ChildError })
}

fn main() {
    match get_super_error() {
        Err(e) => {
            println!("Error: {}", e);
            println!("Caused by: {}", e.source().unwrap());
        }
        _ => println!("No error"),
    }
}
  • ChildError``Error,****source()``std::error::Error
  • CustomError``ChildError,****source(),OptionSome(&self.err)
Error: CustomError is here!
Caused by: ChildError is here!

RustError

6. Error:From

Result(Ok)(Err)match *

  • UTF8
  • u32

()

...

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

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

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

u32

  • unwrap()``bug,panic,
  • matchOK,

:

fn main() {
    let path = "./dat";
    match read_file(path) {
        Ok(v) => {
            match to_utf8(v.as_bytes()) {
                Ok(u) => {
                    match to_u32(u) {
                        Ok(t) => {
                            println!("num:{:?}", u);
                        }
                        Err(e) => {
                            println!("{} {}", path, e)
                        }
                    }
                }
                Err(e) => {
                    println!("{} {}", path, e)
                }
            }
        }
        Err(e) => {
            println!("{} {}", path, e)
        }
    }
}

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

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

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

match****From

Error,errorErrorErrorError****ErrorResultError

#[derive(Debug)]
enum CustomError {
    ParseIntError(std::num::ParseIntError),
    Utf8Error(std::str::Utf8Error),
    IoError(std::io::Error),
}
impl std::error::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 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: std::num::ParseIntError) -> Self {
        CustomError::ParseIntError(s)
    }
}

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

impl From<Utf8Error> for CustomError {
    fn from(s: std::str::Utf8Error) -> Self {
        CustomError::Utf8Error(s)
    }
}
  • CustomErrorError
  • CustomErrorError
  • CustomErrorError Fromtrait,Error

CustomError?

use std::io::Error as IoError;
use std::str::Utf8Error;
use std::num::ParseIntError;
use std::fmt::{Display, Formatter};


fn main() -> std::result::Result<(),CustomError>{
    let path = "./dat";
    let v = read_file(path)?;
    let x = to_utf8(v.as_bytes())?;
    let u = to_u32(x)?;
    println!("num:{:?}",u);
    Ok(())
}

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

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

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


#[derive(Debug)]
enum CustomError {
    ParseIntError(std::num::ParseIntError),
    Utf8Error(std::str::Utf8Error),
    IoError(std::io::Error),
}
impl std::error::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 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: std::num::ParseIntError) -> Self {
        CustomError::ParseIntError(s)
    }
}

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

impl From<Utf8Error> for CustomError {
    fn from(s: std::str::Utf8Error) -> Self {
        CustomError::Utf8Error(s)
    }
}
fn main() -> Result<(),CustomError>{
    let path = "./dat";
    let v = read_file(path)?;
    let x = to_utf8(v.as_bytes())?;
    let u = to_u32(x)?;
    println!("num:{:?}",u);
    Ok(())
}

?``match``?

  • Result<(),CustomError>
  • Err``From``From``CustomError

From``?``match****Err(e)``?

golang:if err!=nil``?

Result``main

  • Result``main
  • Result``Result``unwrap()

Result

...

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_get_num() -> std::result::Result<(), CustomError> {
        let path = "./dat";
        let v = read_file(path)?;
        let x = to_utf8(v.as_bytes())?;
        let u = to_u32(x)?;
        assert_eq!(u, 8);
        Ok(())
    }
}

7. Result

`ResultResultErr```,********

///
fn read_file(path: &str) -> std::result::Result<String, CustomError> {
    let val = std::fs::read_to_string(path)?;
    Ok(val)
}

/// utf8
fn to_utf8(v: &[u8]) -> std::result::Result<&str, CustomError> {
    let x = std::str::from_utf8(v)?;
    Ok(x)
}

/// u32
fn to_u32(v: &str) -> std::result::Result<u32, CustomError> {
    let i = v.parse::<u32>()?;
    Ok(i)
}

****Rust``Result:

pub type IResult<I> = std::result::Result<I, CustomError>; ///ResultIResult

Result``IResult``std::result::Result``Error

///
fn read_file(path: &str) -> IResult<String> {
    let val = std::fs::read_to_string(path)?;
    Ok(val)
}

/// utf8
fn to_utf8(v: &[u8]) -> IResult<&str> {
    let x = std::str::from_utf8(v)?;
    Ok(x)
}

/// u32
fn to_u32(v: &str) -> IResult<u32> {
    let i = v.parse::<u32>()?;
    Ok(i)
}

std::result::Result<I, CustomError> IResult<I>

OK tuple (I,O)

pub type IResult<I, O> = std::result::Result<(I, O), CustomError>;

I,O,

fn foo() -> IResult<String, u32> {
    Ok((String::from("bar"), 32))
}

Result

8. Option

Rust``unwrap()``Result,Option``Option

pub enum Option<T> {
    /// No value
    #[stable(feature = "rust1", since = "1.0.0")]
    None,
    /// Some value `T`
    #[stable(feature = "rust1", since = "1.0.0")]
    Some(#[stable(feature = "rust1", since = "1.0.0")] T),
}

Option``enum``None,Some(T)

Some(T)``T,unwrap()``unwrap()``panic

Ok,, Option``?``?,


#[derive(Debug)]
enum Error {
    OptionError(String),
}

impl std::error::Error for Error {}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match &self {
            Error::OptionError(ref e) => e.fmt(f),
        }
    }
}

pub type Result<I> = std::result::Result<I, Error>;


fn main() -> Result<()> {
    let bar = foo(60)?;
    assert_eq!("bar", bar);
    Ok(())
}

fn foo(index: i32) -> Option<String> {
    if index > 60 {
        return Some("bar".to_string());
    }
    None
}
error[E0277]: `?` couldn't convert the error to `Error`
  --> src/main.rs:22:22
   |
22 |     let bar = foo(60)?;
   |                      ^ the trait `std::convert::From<std::option::NoneError>` is not implemented for `Error`
   |
   = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
   = note: required by `std::convert::From::from`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
error: could not compile `hyper-define`.

std::convert::From<std::option::NoneError>``NoneError``unstable``FromError

Rust``Option``Result``Option``Option``ok_or()``Result

impl<T> Option<T> {
    pub fn ok_or<E>(self, err: E) -> Result<T, E> {
        match self {
            Some(v) => Ok(v),
            None => Err(err),
        }
    }
}    

ok_or()ErrorOption->Result``Result

Option

https://stackoverflow.com/questions/59568278/why-does-the-operator-report-the-error-the-trait-bound-noneerror-error-is-no

9. unwrap()

?``Option``Result``Option

fn main() {
    if let Some(v) = opt_val(60) {
        println!("{}", v);
    }
}

fn opt_val(num: i32) -> Option<String> {
    if num >= 60 {
        return Some("foo bar".to_string());
    }
    None
}

if let Some(v)``else``Option``Result:

fn main() {
    if let Ok(v) = read_file("./dat") {
        println!("{}", v);
    }
}

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

Result``if let Ok(v)``Option``if let Some(v)

unwrap()****

10. Error

Result``?,ErrorGlobalError

Error

std::convert::From<error::GlobalError<A>>` is not implemented for `error::GlobalError<B>

GlobalErrorFrom<GlobalError>GlobalError****

  • T``T
    ErrorInnnerError,ErrorGlobalError``Result<T,InnerError>,Result<T,GlobalError>``From<T>
impl From<InnerError> for GlobalError {
    fn from(s: InnerError) -> Self {
        Error::new(ErrorKind::InnerError(e))
    }
}

Error,Errorstd::result::Result<T,Err>``Err``From<T>``trait

11. Error

Rust

12.

13

https://github.com/Geal/nom

https://github.com/baoyachi/rust-error-handle/blob/master/src/demo_nom_error_handle.rs

14.

ErrorRust

:https://github.com/baoyachi/rust-handle-error/tree/master/src

:https://github.com/baoyachi/rust-error-handle