作为一款把安全发挥到极致的现代化语言,Rust怎会轻易放过程序运行中的错误呢,简单地抛出异常?不存在的,给老子都处理了去。
不同于c#,python等传统语言采用抛出异常的方式处理错误,Rust基于强大的类型,泛型系统,采用Option枚举的方式,在程序运行过程中表达“有”和“无”的概念。
What?
假定你有Rust语法基础,再回顾一下,Option是一个枚举类型,它长这样:
pub enum Option {
None,
Some(T),
}
它的位置:std::option::Option
使用场景
根据std::option
的介绍,Option
有以下7种用法。
- 初始化值
- 作为在整个输入范围没有定义的函数的返回值
- 如果函数可能有错误,可用
Option
作为返回值,用None
代表可能出现的错误。 - 用作
struct
的可选字段 - 用作函数的可选参数
- 空指针
- 用作复杂情况的返回值
初始化值
如果一个变量可能有值,也可能没有,那么可以使用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可用的操作。
可用操作
- match
- if let
- 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,什么都不发生,否则中断程序。
- 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`
- 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
- 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");
- 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);
- 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));
- 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);
- 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);
- 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));
- 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));
- 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);
- 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);
- 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);
- 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));
- 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);
- 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);
- 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);
欢迎指正