目录
先提一个建议如果是通过rust官网入门的话,个人感觉《通过例子学 Rust》会比《Rust 程序设计语言》更好一些。
我这里的例子实际上也是官网上的例子,对于看一遍不太清晰的例子,我会选择自己写下来。
这篇文章假设你已经了解了关于Option类型的一些概念(实际上是rust用来处理空值的工具)。
map方法的使用
需求:假设我想吃一种食物,这个食物需要经过削皮、切块和煮熟这三个线性的流程,此外在这三个流程之前,我还要判断这个原材料是否存在,只有以上条件全部满足,才能达成eat的目标。
我们可以这样去设计:食物本身是一个Option选项,此外每经过上面的一个流程,就可以将食物包裹在一个对应的元组结构体之中。于是我们有了下面的写法:
struct Peeled(String); struct Choped(String); struct Cooked(String); // 削皮 fn peel(food: Option<String>) -> Option<Peeled> { match food { Some(food) => Some(Peeled(food)), None => None, } } // 切块 fn chop(peeled_food: Option<Peeled>) -> Option<Choped> { match peeled_food { Some(Peeled(food)) => Some(Choped(food)), None => None, } } // 烹饪 fn cook(choped_food: Option<Choped>) -> Option<Cooked> { match choped_food { Some(Choped(food)) => Some(Cooked(food)), None => None, } } // 吃 fn eat(food: Option<Cooked>) { match food { Some(Cooked(food)) => println!("俺今天吃了{food}"), None => println!("没吃"), } }
尝试完整走完这个流程
let real_food = Some(String::from("猪头肉")); eat(cook(chop(peel(real_food))));
明显可以看到这里有一个函数的嵌套,不是非常雅观,那么我们可以使用Option类型的map方法对三个处理过程进行改写,改成一个函数叫process_food
fn process_food(food: Option<String>) -> Option<Cooked> { food.map(|f| Peeled(f)) .map(|Peeled(f)| Choped(f)) .map(|Choped(f)| Cooked(f)) }
这个map当中是一个闭包,以第一个闭包为例,它只处理Some的情况,它会将Some(food:String)转换成Some(Peeled(food)),否则直接返回None,当然这里还涉及到一个解构的问题,上面的f实际上全部是函数的参数food包裹的那个String(讲的很抽象)。
可以调用一下,实际上还是能运行的
let real_food1 = Some(String::from("烧鸡")); eat(process_food(real_food1));
and_then方法的使用
需求,有一些食物,我只吃能飞和有腿的,如果符合要求就以Some(food)的形式返回
enum Food { Fish, Chiken, Cow, } // 进行能飞和有腿的检测,能通过的话就用Some包裹起来 fn has_legs(food: Food) -> Option<Food> { match food { Food::Fish => None, _ => Some(food), } } fn can_fly(food: Food) -> Option<Food> { match food { Food::Chiken => Some(food), _ => None, } } fn eat1(food: Option<Food>) { match food { Some(_food) => println!("i can eat it"), None => println!("i am hungury"), } }
将上面的两个检测函数组合成一个
fn test(food: Food) -> Option<Food> { match has_legs(food) { None => None, Some(food) => match can_fly(food) { Some(food) => Some(food), None => None, }, } }
这里的test又变成了一个match的嵌套,这里的检测在流程上没有顺序要求,当然你可以通过改写match的流程来固定顺序,可以用and_then来进行改写
fn test1(food: Food) -> Option<Food> { has_legs(food).and_then(can_fly) }
eat1(test(Food::Chiken)); eat1(test(Food::Fish)); eat1(test1(Food::Cow));
运行起来都是一样的。
这两个方法的用法情境有什么不同呢?恕我才疏学浅,暂时不能用准确的言语进行概括
rust基础学习历程
目前的水平只能说是入门,之前分别在21和22年入门过两次,均是失败告终,一方面rust确实火星,另一方面我自学编程当时只有js基础。
23年初的这次入门终于成功了,原因有二,一是我学了ts和golang+hello world程度的c++,对类型、栈堆、指针之类的概念有了点基础的理解。二是我明白了rust那些火星般的新特点是针对编程中的老问题提出的,从实用角度去理解能更好掌握这些新的特点。
我认为rust的特点是:你会比以往更了解自己写的代码。
希望能有更多人学习这门语言,我也会尽可能以一个业余者的身份更新一些rust或者其他编程的基础知识。