zl程序教程

您现在的位置是:首页 >  其它

当前栏目

Option的正确打开方式

正确 option 打开方式
2023-09-27 14:20:16 时间

Option 的正确打开方式

1. What?

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

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

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

2. 使用场景

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

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

2.1 初始化值

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

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

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

  1. 作为函数返回值
  2. struct有带有Option<T>的字段时,初始化该字段需要创建Option<T>
  3. 作为函数参数

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

#[derive(Debug)]
struct Student {
    name:String,
    year:u8,
    score:Option<f32>//可选的字段
}
impl Student{
  fn new(n:String,y:u8)->Self{
    Self{
      name:n,
      year:y,
      score:None,
    }
  }
  //接收Option作为参数
  fn set_score(&mut self,s:Option<f32>){
    self.score = s;
  }
}
  //返回Option
  fn compute_score(s:f32)->Option<f32>{
        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]

2.2 空指针

此处搬运标准库的例子

Rust的指针指向一定是个合法的内存地址,因此Rust没有空指针,取而代之的是可选指针(optional pointer),比如Option<Box<T>>

下面的栗子使用Option创建一个Option<Box<i32>>,为了使用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<Box<i32>>) {
    match optional {
        Some(ref p) => println!("has value {}", p),
        None => println!("has no value"),
    }
}

上面的例子使用Option创建一个安全的可空指针(nullable pointer),这种Option的用法在Rust中很常见,因此Rust对此做了优化,确保Option<Box>像一个很普通的指针那样高效率,且不带额外的开销。

2.3 复杂情况的返回值

Rust是个静态强类型的语言,特性trait是编译期采用静态分发的方式为每个struct添加方法,因此Rust的抽象没有额外的开销。

当我们调用某个复杂的库,实现其一个带有类型占位符的trait时,我们要指定一个类型,否则编译不过,但是此类型不是随便指定的,它可能需要实现某种trait,因为后续的操作可能要用到某种trait的方法,这时库会给我们提供一些实现某种 trait 的基础类型,比如Option<T>,Result<T,E>等。

来看actix中的一个例子:

不知道什么是actix,actix中文

actix文档,actixDoc

创建一个Actor需要为其结构体实现 trait Actor

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

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

struct WhoAmI;

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

impl Handler<WhoAmI> for MyActor {
    type Result = Option<actix::Addr<MyActor>, ()>;

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

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

说完Option的几个使用场景,下面来看看针对Option可用的操作。

3 可用操作

  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,什么都不发生,否则中断程序。

3.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`

3.2. 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

3.3. 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");

3.4. unwrap_or_else()

  • 说明:
    有值,返回值,否则,执行闭包。
  • 源码:
   pub fn unwrap_or_else<F: FnOnce() -> 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);

3.5. map()

  • 说明:
    改变值,并返回另一个Option。
  • 源码:
   pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> {
        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));

3.6. map_or()

  • 说明:
    有值,则执行闭包返回值,否则返回一个自定义的默认值。
  • 源码:
    pub fn map_or<U, F: FnOnce(T) -> 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);

3.7. map_or_else()

  • 说明:
    有值,执行闭包,否则执行另一个闭包。
  • 源码:
    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(),
        }
    }
  • 栗子:
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);

3.8. ok_or()

  • 说明:
    有值,返回Result,否则返回自定义的错误。
  • 源码:
   pub fn ok_or<E>(self, err: E) -> Result<T, E> {
        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));

3.9. ok_or_else()

  • 说明:
    有值,返回Result,否则执行代表错误的闭包。
  • 源码:
    pub fn ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E> {
        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));

3.10. iter()

  • 说明:
    把Option转换为迭代器。
  • 源码:
    pub fn iter(&self) -> Iter<T> {
        Iter { inner: Item { opt: self.as_ref() } }
    }
  • 栗子:
let x = Some(4);
assert_eq!(x.iter().next(), Some(&4));

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

3.11. and()

  • 说明:
    有值,返回另一Option,否则返回None。
  • 源码:
   pub fn and<U>(self, optb: Option<U>) -> Option<U> {
        match self {
            Some(_) => optb,
            None => None,
        }
    }
  • 栗子:
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);

3.12. and_then()

  • 说明:
    有值,执行闭包,否则返回None。
  • 源码:
   pub fn and_then<U, F: FnOnce(T) -> Option<U>>(self, f: F) -> Option<U> {
        match self {
            Some(x) => f(x),
            None => None,
        }
    }
  • 栗子:
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);

3.13 filter()

  • 说明:
    过滤器,过滤出自己想要的值。
  • 源码:
  pub fn filter<P: FnOnce(&T) -> 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));

3.14 or()

  • 说明:
    有值,返回自身,否则返回自定义的Option。
  • 源码:
pub fn or(self, optb: Option<T>) -> Option<T> {
    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<u32> = None;
let y = None;
assert_eq!(x.or(y), None);

3.15 or_else()

  • 说明:
    有值,返回自身,否则执行闭包。
  • 源码:
pub fn or_else<F: FnOnce() -> Option<T>>(self, f: F) -> Option<T> {
    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);

3.16 take()

  • 说明:
    取出一个值。
  • 源码:
pub fn take(&mut self) -> Option<T> {
    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<u32> = None;
let y = x.take();
assert_eq!(x, None);
assert_eq!(y, None);

欢迎指正!


作者:Nuko
链接:https://www.jianshu.com/p/ce5bddf4b335
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。