[教程]Option的正确打开方式

作为一款把安全发挥到极致的现代化语言,Rust怎会轻易放过程序运行中的错误呢,简单地抛出异常?不存在的,给老子都处理了去。
不同于c#,python等传统语言采用抛出异常的方式处理错误,Rust基于强大的类型,泛型系统,采用Option枚举的方式,在程序运行过程中表达“有”和“无”的概念。

What?

假定你有Rust语法基础,再回顾一下,Option是一个枚举类型,它长这样:

pub enum Option {
    None,
    Some(T),
}

它的位置:std::option::Option

使用场景

根据std::option的介绍,Option有以下7种用法。

  1. 初始化值
  2. 作为在整个输入范围没有定义的函数的返回值
  3. 如果函数可能有错误,可用 Option作为返回值,用None代表可能出现的错误。
  4. 用作struct的可选字段
  5. 用作函数的可选参数
  6. 空指针
  7. 用作复杂情况的返回值

初始化值
如果一个变量可能有值,也可能没有,那么可以使用Option初始化该变量。

let s = Some(3);
if let Some(ref s) = s{
  println!("{:?}",*s);
}

有或没有值所造成的影响,只有在使用该变量时才体现出来,否则只是println!()没必要初始化一个Option,能使用一个Option的情况有几种,分别是:

  • 作为函数返回值
  • struct有带有Option的字段时,初始化该字段需要创建Option
  • 作为函数参数

通过一个set_score()struct Student设置值

#[derive(Debug)]
struct Student {
    name:String,
    year:u8,
    score:Option//可选的字段
}
impl Student{
  fn new(n:String,y:u8)->Self{
    Self{
      name:n,
      year:y,
      score:None,
    }
  }
  //接收Option作为参数
  fn set_score(&mut self,s:Option){
    self.score = s;
  }
}
  //返回Option
  fn compute_score(s:f32)->Option{
        let d = s*0.75;
        Some(d)
  }
fn main(){
    let mut d = Student::new("xiaoming".to_string(),18);
    dbg!(&d.score);
    let score = compute_score(100.0);
    d.set_score(score);
    dbg!(&d.score);
}
[Running: cargo run --message-format=json]
   Compiling study v0.1.0 (/home/banapy/projects/study)
    Finished dev [unoptimized + debuginfo] target(s) in 0.65s
     Running `target/debug/study`
[src/main.rs:25] &d.score = None
[src/main.rs:28] &d.score = Some(
    75.0
)
[Finished in 0.8s]

空指针
此处搬运标准库的例子
Rust的指针指向一定是个合法的内存地址,因此Rust没有空指针,取而代之的是可选指针(optional pointer),比如Option>
下面的栗子使用Option创建一个Option>,为了使用i32的值,check_option函数使用了模式匹配,判断box里是否有值,因为Option也可能没值嘛。

let optional = None;
check_optional(optional);

let optional = Some(Box::new(9000));
check_optional(optional);
//接收Option作为参数
fn check_optional(optional: Option>) {
    match optional {
        Some(ref p) => println!("has value {}", p),
        None => println!("has no value"),
    }
}

上面的例子使用Option创建一个安全的可空指针(nullable pointer),这种Option的用法在Rust中很常见,因此Rust对此做了优化,确保Option>像一个很普通的指针那样高效率,且不带额外的开销。
复杂情况的返回值
Rust是个静态强类型的语言,特性trait是编译期采用静态分发的方式为每个struct添加方法,因此Rust的抽象没有额外的开销。
当我们调用某个复杂的库,实现其一个带有类型占位符的trait时,我们要指定一个类型,否则编译不过,但是此类型不是随便指定的,它可能需要实现某种trait,因为后续的操作可能要用到某种trait的方法,这时库会给我们提供一些实现某种trait的基础类型,比如Option,Result等。
来看actix中的一个例子:
不知道什么是actix,actix中文
actix文档,actixDoc
创建一个Actor需要为其结构体实现 trait Actor

extern crate actix;
use actix::prelude::*;
struct MyActor;
impl Actor for MyActor {
    type Context = Context;
}

Actor之间通过消息进行交流,当Actor接收一条消息后,我们要定义一个handle处理这条消息。

struct WhoAmI;

impl Message for WhoAmI {
    //此处指定为Option>类型
    type Result = Option, ()>;
}

impl Handler for MyActor {
    type Result = Option, ()>;

    fn handle(&mut self, msg: WhoAmI, ctx: &mut Context) -> Self::Result {
        Ok(ctx.address())
    }
}

消息处理都有一个结果,由于Rust的静态强类型,我们必须为结果指定一个数据类型type Result=Option,这条数据类型不是随便指定的,它要实现trait MessageResponse,但是我们太懒了,不想费时间实现这个trait,不实现又不行,这时可以使用actix库提供的基础类型,这些类型都实现了MessageResponse,比如Option,这时,Option就能在这种复杂情况下带出要返回的值。
当然复杂情况不止这一种,欢迎总结。
说完Option的几个使用场景,下面来看看针对Option可用的操作。

可用操作

  1. match
  2. if let
  3. unwrap()等组合器

match是个很强大的模式匹配,很适合匹配Option等枚举类型。

fn main(){
    let d = Student::new("xiaoming".to_string(),18);
    let d = Some(d);
   match d{
    Some(s)=>{
      println!("{:?}", s.name);
    }
    None=>{
      println!("None");
    }
  }
}

match简单易操作,只是有点冗长,众所周知,程序员是相当的“懒”,不想写一点多余的代码,在很多情况下我们并不关心错误值是什么,只想用Option里的值,这时一个简单的if let语句给我们省下很多事。

fn main(){
    let d = Student::new("xiaoming".to_string(),18);
    let d = Some(&d);
    let num = if let Some(s) = d{
    println!("{:?}", s.name);
    Some(3)
  }
}

Option的一些基本操作,相信大家一看就懂,这里不再赘述,下面重点看下Option的组合器。这种写法属于函数式编程,刚接触的人肯定对map, map_or,map_or_else很困惑。
所谓的函数式是一种编程范式,Rust有函数式编程的支持,不代表Rust是函数式编程语言。
想了解函数式编程?
函数式编程--百度百科
下面以说明,源码,例子的顺序,一一介绍这些好玩的东东:
注意:断言assert_eq!(a,b),如果a==b,什么都不发生,否则中断程序。

  1. expect()
  • 说明:
    有值,返回值,否则,中断程序,打印msg错误信息。

  • 源码:

    pub fn expect(self, msg: &str) -> T {
        match self {
            Some(val) => val,
            None => expect_failed(msg),
        }
    }
  • 栗子:
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`
  1. unwrap()
  • 说明:
    有值,返回值,否则,中断程序。

  • 源码:

    pub fn unwrap(self) -> T {
        match self {
            Some(val) => val,
            None => panic!("called `Option::unwrap()` on a `None` value"),
        }
    }
  • 栗子:
let x = Some("air");
assert_eq!(x.unwrap(), "air");
let x: Option<&str> = None;
assert_eq!(x.unwrap(), "air"); // fails
  1. unwrap_or()
  • 说明:
    有值,返回值,否则返回一个默认值。
  • 源码:
    pub fn unwrap_or(self, def: T) -> T {
        match self {
            Some(x) => x,
            None => def,
        }
    }
  • 栗子:
assert_eq!(Some("car").unwrap_or("bike"), "car");
assert_eq!(None.unwrap_or("bike"), "bike");
  1. unwrap_or_else()
  • 说明:
    有值,返回值,否则,执行闭包。
  • 源码:
    pub fn unwrap_or_else T>(self, f: F) -> T {
        match self {
            Some(x) => x,
            None => f(),
        }
    }
  • 栗子:
let k = 10;
assert_eq!(Some(4).unwrap_or_else(|| 2 * k), 4);
assert_eq!(None.unwrap_or_else(|| 2 * k), 20);
  1. map()
  • 说明:
    改变值,并返回另一个Option。
  • 源码:
    pub fn map U>(self, f: F) -> Option {
        match self {
            Some(x) => Some(f(x)),
            None => None,
        }
    }
  • 栗子:
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));
  1. map_or()
  • 说明:
    有值,则执行闭包返回值,否则返回一个自定义的默认值。
  • 源码:
    pub fn map_or U>(self, default: U, f: F) -> U {
        match self {
            Some(t) => f(t),
            None => default,
        }
    }
  • 栗子:
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);
  1. map_or_else()
  • 说明:
    有值,执行闭包,否则执行另一个闭包。
  • 源码:
    pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U {
        match self {
            Some(t) => f(t),
            None => default(),
        }
    }
  • 栗子:
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);
  1. ok_or()
  • 说明:
    有值,返回Result,否则返回自定义的错误。
  • 源码:
    pub fn ok_or(self, err: E) -> Result {
        match self {
            Some(v) => Ok(v),
            None => Err(err),
        }
    }
  • 栗子:
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));
  1. ok_or_else()
  • 说明:
    有值,返回Result,否则执行代表错误的闭包。
  • 源码:
    pub fn ok_or_else E>(self, err: F) -> Result {
        match self {
            Some(v) => Ok(v),
            None => Err(err()),
        }
    }
  • 栗子:
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));
  1. iter()
  • 说明:
    把Option转换为迭代器。
  • 源码:
    pub fn iter(&self) -> Iter {
        Iter { inner: Item { opt: self.as_ref() } }
    }
  • 栗子:
let x = Some(4);
assert_eq!(x.iter().next(), Some(&4));

let x: Option = None;
assert_eq!(x.iter().next(), None);
  1. and()
  • 说明:
    有值,返回另一Option,否则返回None。
  • 源码:
    pub fn and(self, optb: Option) -> Option {
        match self {
            Some(_) => optb,
            None => None,
        }
    }

  • 栗子:
let x = Some(2);
let y: Option<&str> = None;
assert_eq!(x.and(y), None);

let x: Option = 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 = None;
let y: Option<&str> = None;
assert_eq!(x.and(y), None);
  1. and_then()
  • 说明:
    有值,执行闭包,否则返回None。
  • 源码:
    pub fn and_then Option>(self, f: F) -> Option {
        match self {
            Some(x) => f(x),
            None => None,
        }
    }
  • 栗子:
fn sq(x: u32) -> Option { Some(x * x) }
fn nope(_: u32) -> Option { 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);
  1. filter()
  • 说明:
    过滤器,过滤出自己想要的值。
  • 源码:
    pub fn filter bool>(self, predicate: P) -> Self {
        if let Some(x) = self {
            if predicate(&x) {
                return Some(x)
            }
        }
        None
    }
  • 栗子:
fn is_even(n: &i32) -> bool {
    n % 2 == 0
}

assert_eq!(None.filter(is_even), None);
assert_eq!(Some(3).filter(is_even), None);
assert_eq!(Some(4).filter(is_even), Some(4));
  1. or()
  • 说明:
    有值,返回自身,否则返回自定义的Option。
  • 源码:
    pub fn or(self, optb: Option) -> Option {
        match self {
            Some(_) => self,
            None => optb,
        }
    }
  • 栗子:
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 = None;
let y = None;
assert_eq!(x.or(y), None);
  1. or_else()
  • 说明:
    有值,返回自身,否则执行闭包。
  • 源码:
    pub fn or_else Option>(self, f: F) -> Option {
        match self {
            Some(_) => self,
            None => f(),
        }
    }
  • 栗子:
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);
  1. take()
  • 说明:
    取出一个值。
  • 源码:
    pub fn take(&mut self) -> Option {
        mem::replace(self, None)
    }
  • 栗子:
let mut x = Some(2);
let y = x.take();
assert_eq!(x, None);
assert_eq!(y, Some(2));

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

欢迎指正

你可能感兴趣的:([教程]Option的正确打开方式)