Rust 中的函数与闭包

archive time: 2024-07-27

这个算是刚接触 Rust 时最不适应的地方

Rust 最出名的地方就是其一直宣传的内存安全性,各种设计,包括所有权设计,都是为了内存安全服务,但是这就必然导致 Rust 在灵活性上要比其他语言要差一些

为什么用 Rust

刚开始写 Rust,最主要的就是被 MozillaML 这个外号吸引了,认为 Rust 就是一种语法比较另类的 ML 语言

事实也是,Rust 有许多函数式的性质,并且 enum 的设计以及模式匹配,无一不在加深“Rust 就是 ML 语言”的印象

但是当接触到 Rust 中的闭包,一下就意识到 Rust 就是 Rust

函数和闭包

除了像 C++ 和 Java 这种临时添加了函数式特性的,或者像 C 这种比较老的语言,大部分现代编程语言或多或少都支持闭包的性质,例如 Swift,Ruby

在这些支持闭包的语言中,一般都会说“函数就是一种特殊的全局闭包”,例如 Swift

但是 Rust 却不一样,在某些情况下,函数和闭包可以换着用,有的时候不行

而且闭包在使用和运行过程中和 Java 做法类似,即创建了一个 Closure 对象(Java 是 Lambda 对象),这个对象的类型是独有的,所以不能和函数通用1

而且不同的泛型下,例如对于 foo<U>(),其中 foo::<i32>foo::<u32> 是不同的类型

而且由于有所有权的概念,有些情况下,所有权检查器(borrow checker)会认为你的闭包“活不久”或者“所有权已经被转移”,这也是常见的错误

解决方法

但是 Rust 还算是支持函数式编程的,要怎么做呢?

这就要使用到 Rust 中的另一个内容, trait

函数类型都实现了 FnFnMutFnOneceCopyCloneSendSync 这些 trait2,所以在想要既可以接收函数,又可以接收闭包的函数中,可以使用 impl Fn,或者使用 Box<dyn Fn> 也是可以的

Fn,FnMut 还是 FnOnce

但是 trait 这么多,该用哪一个呢?

一般来说,用 Fn 就可以了,但是如果你的函数的状态会修改,那就需要使用 FnMut,如果你确定你的函数指使用一次,那就可以使用 FnOnce

Fn 作为参数

fn call_with_one<F>(func: F) -> usize
    where F: Fn(usize) -> usize {
    func(1)
}

let double = |x| x * 2;
assert_eq!(call_with_one(double), 2);

FnMut 作为参数

fn do_twice<F>(mut func: F)
    where F: FnMut()
{
    func();
    func();
}

let mut x: usize = 1;
{
    let add_two_to_x = || x += 2;
    do_twice(add_two_to_x);
}

assert_eq!(x, 5);

FnOnce 作为参数

fn consume_with_relish<F>(func: F)
    where F: FnOnce() -> String
{
    // `func` consumes its captured variables, so it cannot be run more
    // than once.
    println!("Consumed: {}", func());

    println!("Delicious!");

    // Attempting to invoke `func()` again will throw a `use of moved
    // value` error for `func`.
}

let x = String::from("x");
let consume_and_return_x = move || x;
consume_with_relish(consume_and_return_x);

// `consume_and_return_x` can no longer be invoked at this point

Rust 的函数和闭包就是两种东西,但是我认为应该是可以变成一种的,即对于函数而言,捕获的环境是空的,闭包不是,检查的时候先检查捕获环境,再逐层向上检查即可

Rust 不这样设计一定有它自己的理由


1

Closure types.The Rust Reference [DB/OL].(2021-07-27)[2024-07-27]. https://doc.rust-lang.org/reference/types/closure.html

2

Function item types.The Rust Reference [DB/OL].(2019-07-16)[2024-07-27]. https://doc.rust-lang.org/reference/types/function-item.html