Rust 学习 11

archive time: 2022-06-19

这回来学一下函数式相关的东西

Rust 有一个比较重要的特性就是函数式范式, 说到函数式, 大多数人想到的应该就是 lambda 表达式

Rust 里也有类似的东西, 那就是闭包 (closure)

闭包

闭包, 可以看成是其他语言的匿名函数, 但是结合 Rust 的所有权的特性, 又有所不同

闭包是一个可以 捕获其所在环境 的 匿名函数

这里的捕获其所在环境就是说闭包会 借用 用到的闭包定义外的值, 如果要获取所有权需要使用 move 关键字

pub fn derv<'a, F>(f: &'a F) -> Box<dyn Fn(f64) -> f64 + 'a>
where
    F: Fn(f64) -> f64 + 'a,
{
    let dx = 1e-6;
    Box::new(move |x: f64| -> f64 { (f(x + dx) - f(x)) / dx })
}

这里, 我们使用 Box 包裹来返回一个闭包1, 而在返回时用到了函数内定义的 dx 这个变量

如果不获取其所有权, 那么在函数结束时, dx 将会被释放, 即 dx 这个值就会无效

所以我们使用 move 关键字表明我们获取了 dx 的所有权

类型推导

闭包和函数的一个区别就是, 在定义闭包的时候, 我们不一定需要标注参数和返回值的类型, Rust 一定程度上可以进行类型推断

let f = |x| x + 1;
println!("{}", f(4));

这里我们没有标注类型, 但是 Rust 通过上下文推断知道 x 的类型应该是 i32, 而返回值类型也是 i32

struct 里的闭包

在上面的例子里我们看到了闭包可以像值, 或者说闭包就是一个值, 从函数返回, 那么, 我们自然可以在 struct 里定义闭包类型的字段

不过在这里, 我们必须指明闭包的类型, 这就要说到 Rust 里的函数类型 Fn, FnMutFnOnce

具体的定义这里可以参考官方文档2

pub struct Cacher<T>
where
    T: Fn(u32) -> u32
{
    calc: T,
    val: Option<u32>,
}

impl<T> Cacher<T>
where
    T: Fn(u32) -> u32
{
    pub fn new(calc: T) -> Self<T> {
        Cacher {
            calc,
            val: None,
        }
    }

    pub fn value(&mut self, arg: u32) -> u32 {
        match self.val {
            Some(v) => v,
            None => {
                let v = (self.calc)(arg);
                self.val = Some(v);
                v
            }
        }
    }
}

以上只是个例子, 函数逻辑只是示例

迭代器

迭代器模式: 对一系列项执行某些任务

迭代器可以遍历每一个项, 并且确定序列合适完成

在 Rust 里, 迭代器是 惰性的(lazy), 即调用 消耗性适配器 前, 本身没有效果

所有的迭代器都实现了 Iterator trait

pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
    /// ...
}

这里, next() 方法的作用就是返回下一项, 如果没有下一项, 那么就是 None

一般获取迭代器的方法就是 iter(), 其次就是 into_iter(), 会获取所有权, iter_mut() 则会迭代可变引用

其实这里和 Java 的流 (Stream) 很类似, 可以参考 Java


好, 又摸了一章, 继续摆!

暑假后再好好学习

1

文档可以参考这里

2

可以参考文档 Fn, FnMut, FnOnce