Summary of error handling operators in Rust

Keywords: Rust

1 Option and Result

Option < T > is used to indicate whether there is a value. When there is a value, it is Some(T); Otherwise, None.

enum Option<T> {
    None,
    Some(T),
}

Result < T, E > is used to indicate whether an error has occurred. When no error has occurred, it is Ok(T); Otherwise, Err(E).

enum Result<T, E> {
    Ok(T),
    Err(E),
}

The following explains how to handle Option and Result.

2 processing Option

2.1 panic occurs when there is no value

2.1.1 expect

  • Value, return value; Otherwise, interrupt the program and print msg error message.
  • Source code
pub fn expect(self, msg: &str) -> T {
      match self {
      Some(val) => val,
      None => expect_failed(msg),
  }
  • example
let x = Some("value");
assert_eq!(x.expect("the world is ending"), "value");
let x: Option<&str> = None;
x.expect("the world is ending"); // panics with `the world is ending`

2.1.2 unwrap

  • Value, return value; Otherwise, interrupt the program.
  • Source code
pub fn unwrap(self) -> T {
    match self {
        Some(val) => val,
        None => panic!("called `Option::unwrap()` on a `None` value"),
    }
}
  • example
let x = Some("air");
assert_eq!(x.unwrap(), "air");
let x: Option<&str> = None;
assert_eq!(x.unwrap(), "air"); // fails

2.2 return another value when there is no value

2.2.1 unwrap_or

  • Value, return value; Otherwise, a default value is returned.
  • Source code
pub fn unwrap_or(self, def: T) -> T {
    match self {
        Some(x) => x,
        None => def,
    }
}
  • example
assert_eq!(Some("car").unwrap_or("bike"), "car");
assert_eq!(None.unwrap_or("bike"), "bike");
assert_eq!(None.unwrap_or(2), 2);

2.2.2 unwrap_or_else

  • Value, return value; Otherwise, execute the closure.
  • Source code
pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
    match self {
        Some(x) => x,
        None => f(),
    }
}
  • example
let k = 10;
assert_eq!(Some(4).unwrap_or_else(|| 2 * k), 4);
assert_eq!(None.unwrap_or_else(|| 2 * k), 20);
assert_eq!(None.unwrap_or_else(|| "hello"), "hello");

2.2.3 map_or

  • If there is a value, execute the closure return value; Otherwise, a custom default value is returned.
  • Source code
pub fn map_or<U, F: FnOnce(T) -> U>(self, default: U, f: F) -> U {
    match self {
        Some(t) => f(t),
        None => default,
    }
}
  • example
let x = Some("foo");
assert_eq!(x.map_or(42, |v| v.len()), 3);

let x: Option<&str> = None;
assert_eq!(x.map_or(42, |v| v.len()), 42);

2.2.4 map_or_else

  • If there is value, execute closure; Otherwise, another closure is executed.
  • Source code
pub fn map_or_else<U, D: FnOnce() -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U {
    match self {
        Some(t) => f(t),
        None => default(),
    }
}
  • example
let k = 21;

let x = Some("foo");
assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3);

let x: Option<&str> = None;
assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42);

2.3 return Option when there is no value

2.3.1 map

  • Change the value and return another Option.
  • Source code
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> {
    match self {
        Some(x) => Some(f(x)),
        None => None,
    }
}
  • example
let maybe_some_string = Some(String::from("Hello, World!"));
// `Option::map` takes self *by value*, consuming `maybe_some_string`
let maybe_some_len = maybe_some_string.map(|s| s.len());

assert_eq!(maybe_some_len, Some(13));

2.3.2 and

  • If there is a value, another Option is returned; Otherwise, return to None.
  • Source code
pub fn and<U>(self, optb: Option<U>) -> Option<U> {
    match self {
        Some(_) => optb,
        None => None,
    }
}
  • example
let x = Some(2);
let y: Option<&str> = None;
assert_eq!(x.and(y), None);

let x: Option<u32> = None;
let y = Some("foo");
assert_eq!(x.and(y), None);

let x = Some(2);
let y = Some("foo");
assert_eq!(x.and(y), Some("foo"));

let x: Option<u32> = None;
let y: Option<&str> = None;
assert_eq!(x.and(y), None);

2.3.3 and_then

  • If there is value, execute closure; Otherwise, return to None.
  • Source code
pub fn and_then<U, F: FnOnce(T) -> Option<U>>(self, f: F) -> Option<U> {
    match self {
        Some(x) => f(x),
        None => None,
    }
}
  • example
fn sq(x: u32) -> Option<u32> { Some(x * x) }
fn nope(_: u32) -> Option<u32> { None }

assert_eq!(Some(2).and_then(sq).and_then(sq), Some(16));
assert_eq!(Some(2).and_then(sq).and_then(nope), None);
assert_eq!(Some(2).and_then(nope).and_then(sq), None);
assert_eq!(None.and_then(sq).and_then(sq), None);

2.3.4 or

  • If there is a value, return itself; Otherwise, the custom Option is returned.
  • Source code
pub fn or(self, optb: Option<T>) -> Option<T> {
    match self {
        Some(_) => self,
        None => optb,
    }
}
  • example
let x = Some(2);
let y = None;
assert_eq!(x.or(y), Some(2));

let x = None;
let y = Some(100);
assert_eq!(x.or(y), Some(100));

let x = Some(2);
let y = Some(100);
assert_eq!(x.or(y), Some(2));

let x: Option<u32> = None;
let y = None;
assert_eq!(x.or(y), None);

2.3.5 or_else

  • If there is a value, return itself; Otherwise, the closure is executed.
  • Source code
pub fn or_else<F: FnOnce() -> Option<T>>(self, f: F) -> Option<T> {
    match self {
        Some(_) => self,
        None => f(),
    }
}
  • example
fn nobody() -> Option<&'static str> { None }
fn vikings() -> Option<&'static str> { Some("vikings") }

assert_eq!(Some("barbarians").or_else(vikings), Some("barbarians"));
assert_eq!(None.or_else(vikings), Some("vikings"));
assert_eq!(None.or_else(nobody), None);

2.3.6 take

  • Take out a value.
  • Source code
pub fn take(&mut self) -> Option<T> {
    mem::replace(self, None)
}
  • example
let mut x = Some(2);
let y = x.take();
assert_eq!(x, None);
assert_eq!(y, Some(2));

let mut x: Option<u32> = None;
let y = x.take();
assert_eq!(x, None);
assert_eq!(y, None);

2.4 return Result when there is no value

2.4.1 ok_or

  • If there is a value, return Result; Otherwise, a custom error is returned.
  • Source code
pub fn ok_or<E>(self, err: E) -> Result<T, E> {
    match self {
        Some(v) => Ok(v),
        None => Err(err),
    }
}
  • example
let x = Some("foo");
assert_eq!(x.ok_or(0), Ok("foo"));

let x: Option<&str> = None;
assert_eq!(x.ok_or(0), Err(0));

2.4.2 ok_or_else

  • If there is a value, return Result; Otherwise, the closure representing the error is executed.
  • Source code
pub fn ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E> {
    match self {
        Some(v) => Ok(v),
        None => Err(err()),
    }
}
  • example
let x = Some("foo");
assert_eq!(x.ok_or_else(|| 0), Ok("foo"));

let x: Option<&str> = None;
assert_eq!(x.ok_or_else(|| 0), Err(0));

3 processing Result

3.1 panic in error

3.1.1 expect

  • Correct, return value; Otherwise, interrupt the program and print msg error message.
  • Source code
pub fn expect(self, msg: &str) -> T {
    match self {
        Ok(t) => t,
        Err(e) => unwrap_failed(msg, &e),
    }
}
  • example
let ok: Result<&'static str, anyhow::Error> = Ok("value");
assert_eq!(ok.expect("the world is ending"), "value");

let err: Result<&'static str, anyhow::Error> = Err(anyhow::anyhow!("error"));
err.expect("the world is ending"); // panics with `the world is ending`

3.1.2 unwrap

  • Correct, return value; Otherwise, interrupt the program.
  • Source code
pub fn unwrap(self) -> T {
    match self {
        Ok(t) => t,
        Err(e) => unwrap_failed("called `Result::unwrap()` on an `Err` value", &e),
    }
}
  • example
let ok: Result<&'static str, anyhow::Error> = Ok("value");
assert_eq!(ok.unwrap(), "value");

let err: Result<&'static str, anyhow::Error> = Err(anyhow::anyhow!("error"));
err.unwrap(); // fails

3.2 return another value in case of error

3.2.1 unwrap_or_default

  • Correct, return value; Otherwise, the default value is returned.
  • Source code
pub fn unwrap_or_default(self) -> T {
    match self {
        Ok(x) => x,
        Err(_) => Default::default(),
    }
}
  • example
let good_year_from_input = "1909";
let good_year = good_year_from_input.parse().unwrap_or_default();
assert_eq!(1909, good_year);

let bad_year_from_input = "190blarg";
let bad_year = bad_year_from_input.parse().unwrap_or_default();
assert_eq!(0, bad_year);

3.2.2 unwrap_or

  • Correct, return value; Otherwise, the custom default value is returned.
  • Source code
pub fn unwrap_or(self, default: T) -> T {
    match self {
        Ok(t) => t,
        Err(_) => default,
    }
}
  • example
let ok: Result<&'static str, anyhow::Error> = Ok("value");
assert_eq!(ok.unwrap_or("other"), "value");

let err: Result<&'static str, anyhow::Error> = Err(anyhow::anyhow!("error"));
assert_eq!(err.unwrap_or("other"), "other");

3.2.3 unwrap_or_else

  • Correct, return value; Otherwise, execute the closure.
  • Source code
pub fn unwrap_or_else<F: FnOnce(E) -> T>(self, op: F) -> T {
    match self {
        Ok(t) => t,
        Err(e) => op(e),
    }
}
  • example
let ok: Result<&'static str, anyhow::Error> = Ok("value");
assert_eq!(ok.unwrap_or_else(|v| "other"), "value");

let err: Result<&'static str, anyhow::Error> = Err(anyhow::anyhow!("error"));
assert_eq!(err.unwrap_or_else(|v| "other"), "other");

3.2.4 map_or

  • If it is correct, execute the closure return value; Otherwise, a custom default value is returned.
  • Source code
pub fn map_or<U, F: FnOnce(T) -> U>(self, default: U, f: F) -> U {
    match self {
        Ok(t) => f(t),
        Err(_) => default,
    }
}
  • example
let ok: Result<&'static str, anyhow::Error> = Ok("value");
assert_eq!(ok.map_or(42, |v| v.len()), 5);

let err: Result<&'static str, anyhow::Error> = Err(anyhow::anyhow!("error"));
assert_eq!(err.map_or(42, |v| v.len()), 42);

3.2.5 map_or_else

  • If it is correct, execute the second closure; Otherwise, execute the first closure.
  • Source code
pub fn map_or_else<U, D: FnOnce(E) -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U {
    match self {
        Ok(t) => f(t),
        Err(e) => default(e),
    }
}
  • example
let k = 21;

let ok: Result<&'static str, anyhow::Error> = Ok("value");
assert_eq!(ok.map_or_else(|v| 2 * k, |v| v.len()), 5);

let err: Result<&'static str, anyhow::Error> = Err(anyhow::anyhow!("error"));
assert_eq!(err.map_or_else(|v| 2 * k, |v| v.len()), 42);

3.3 return Result in case of error

3.3.1 map

  • If correct, change the value and return another Result.
  • Source code
pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> Result<U, E> {
    match self {
        Ok(t) => Ok(op(t)),
        Err(e) => Err(e),
    }
}
  • example
let ok: Result<&'static str, anyhow::Error> = Ok("value");
assert_eq!(ok.map(|s| s.len()).unwrap(), 5);

3.3.2 map_err

  • If there is an error, execute a closure on the error and return another Result.
  • Source code
pub fn map_err<F, O: FnOnce(E) -> F>(self, op: O) -> Result<T, F> {
    match self {
        Ok(t) => Ok(t),
        Err(e) => Err(op(e)),
    }
}
  • example
fn stringify(x: u32) -> String { format!("error code: {}", x) }

let x: Result<u32, u32> = Ok(2);
assert_eq!(x.map_err(stringify), Ok(2));

let x: Result<u32, u32> = Err(13);
assert_eq!(x.map_err(stringify), Err("error code: 13".to_string()));

3.3.2 and

  • If it is correct, another Result is returned.
  • Source code
pub fn and<U>(self, res: Result<U, E>) -> Result<U, E> {
    match self {
        Ok(_) => res,
        Err(e) => Err(e),
    }
}
  • example
let ok: Result<u32, &str> = Ok(2);
let x1: Result<u32, &str> = Ok(3);
assert_eq!(ok.and(x1), Ok(3));

let err: Result<u32, &str> = Err("error 1");
let x2: Result<u32, &str> = Err("early 2");
assert_eq!(err.and(x2), Err("error 1"));

3.3.3 and_then

  • If correct, execute the closure.
  • Source code
pub fn and_then<U, F: FnOnce(T) -> Result<U, E>>(self, op: F) -> Result<U, E> {
    match self {
        Ok(t) => op(t),
        Err(e) => Err(e),
    }
}
  • example
fn sq(x: u32) -> Result<u32, u32> { Ok(x * x) }
fn err(x: u32) -> Result<u32, u32> { Err(x) }

assert_eq!(Ok(2).and_then(sq).and_then(sq), Ok(16));
assert_eq!(Ok(2).and_then(sq).and_then(err), Err(4));
assert_eq!(Ok(2).and_then(err).and_then(sq), Err(2));
assert_eq!(Err(3).and_then(sq).and_then(sq), Err(3));

3.3.4 or

  • Error, another Result is returned.
  • Source code
pub fn or<F>(self, res: Result<T, F>) -> Result<T, F> {
    match self {
        Ok(v) => Ok(v),
        Err(_) => res,
    }
}
  • example
let x: Result<u32, &str> = Ok(2);
let y: Result<u32, &str> = Err("late error");
assert_eq!(x.or(y), Ok(2));

let x: Result<u32, &str> = Err("not a 2");
let y: Result<u32, &str> = Err("late error");
assert_eq!(x.or(y), Err("late error"));

3.3.5 or_else

  • If there is an error, the closure is executed.
  • Source code
pub fn or_else<F, O: FnOnce(E) -> Result<T, F>>(self, op: O) -> Result<T, F> {
    match self {
        Ok(t) => Ok(t),
        Err(e) => op(e),
    }
}
  • example
fn sq(x: u32) -> Result<u32, u32> { Ok(x * x) }
fn err(x: u32) -> Result<u32, u32> { Err(x) }

assert_eq!(Ok(2).or_else(sq).or_else(sq), Ok(2));
assert_eq!(Ok(2).or_else(err).or_else(sq), Ok(2));
assert_eq!(Err(3).or_else(sq).or_else(err), Ok(9));
assert_eq!(Err(3).or_else(err).or_else(err), Err(3));

3.4 return Option

3.4.1 ok

  • Value, return value; Otherwise, return to None.
  • Source code
pub fn ok(self) -> Option<T> {
    match self {
        Ok(x) => Some(x),
        Err(_) => None,
    }
}
  • example
let ok: Result<u32, anyhow::Error> = Ok(2);
assert_eq!(ok.ok(), Some(2));

let err: Result<u32, anyhow::Error> = Err(anyhow::anyhow!("error"));
assert_eq!(err.ok(), None);

3.4.r err

  • If there is a value, return None.
  • Source code
pub fn err(self) -> Option<E> {
    match self {
        Ok(_) => None,
        Err(x) => Some(x),
    }
}
  • example
let x: Result<u32, &str> = Ok(2);
assert_eq!(x.err(), None);

let x: Result<u32, &str> = Err("Nothing here");
assert_eq!(x.err(), Some("Nothing here"));

4 combine Option and Result

The above processing operators can be combined.

example:

use std::env;

fn double_arg(mut argv: env::Args) -> Result<i32, String> {
    argv.nth(1)
        .ok_or("Please give at least one argument".to_owned())
        .and_then(|arg| arg.parse::<i32>().map_err(|err| err.to_string()))
        .map(|n| 2 * n)
}

fn main() {
    match double_arg(env::args()) {
        Ok(n) => println!("{}", n),
        Err(err) => println!("Error: {}", err),
    }
}

As mentioned in section 2.4.1, Option is ok_or() returns Result, so OK is available_ Or() converts Option to Result, and then you can continue to use the processing operator of Result.

You can also use the ok() or err() of the Result to convert the Result to Option.

reference resources

https://www.jianshu.com/p/ce5bddf4b335

https://wiki.jikexueyuan.com/project/rust-primer/error-handling/option-result.html

Posted by asolell on Sat, 04 Dec 2021 11:58:19 -0800