rust 笔记3:Trait Cheatsheet

1. Fn, FnMut, FnOnce

1.1. 讨论的范畴:闭包 or 通常函数

讨论这三个 trait 时,通常是关于闭包的。普通的 rust 函数被视为实现了以上所有三个trait。可以用下面这段代码证明:

fn apply_fn<T>(value: T, f: impl Fn(T) -> T) -> T {
    f(value)
}

fn apply_fnmut<T>(value: T, mut f: impl FnMut(T) -> T) -> T {
    f(value)
}

fn apply_fnonce<T>(value: T, f: impl FnOnce(T) -> T) -> T {
    f(value)
}

fn square(num: i32) -> i32 {
    num * num
}

fn main() {
    let result = apply_fn(5, square);
    let result = apply_fnmut(5, square);
    let result = apply_fnonce(5, square);

    println!("{}", result);
}

1.2. 三个 Fn 系列 trait 的区别

对于闭包来说,编译期会自动为他们实现合适的 Fn 系列 trait。分三种:

  • FnOnce 约束最强,表示闭包只能被执行一次,通常是由于取得了捕获值的所有权,或者闭包本身会被消耗
  • FnMut 约束次强,表示闭包可以被多次调用,且持有捕获变量的可变引用。
  • Fn 约束最弱,表示闭包可以被重复调用,且持有捕获变量的不可变引用。

实际上,FnOnceFnMut 的超集(supertrait),而 FnMut 又是 Fn 的超集。即可以用伪码理解:

trait FnOnce {}
trait FnMut: FnOnce {}
trait Fn: FnMut {}

因此,一个较弱的闭包可以被传入更强的约束中,例如一个只实现了 Fn 的闭包可以被作为参数传入要求是 FnOnce 的场合。从逻辑来说,这样也是完全成立的。

1.3. 关于 FnOnce 的补充:调用后被 move,以及闭包实现 Copy

Rust 编译器是怎么限制 FnOnce 只能调用一次的呢?在 rustbook 中译版对闭包的介绍章节中,有一个尝试重复调用的例子

fn fn_once<F>(func: F)
where
    F: FnOnce(usize) -> bool,
{
    println!("{}", func(3));
    println!("{}", func(4));
}

这段代码会报错:

error[E0382]: use of moved value: `func`
   --> src/main.rs:118:20
    |
113 | fn fn_once<F>(func: F)
    |               ---- move occurs because `func` has type `F`, which does not implement the `Copy` trait
...
117 |     println!("{}", func(3));
    |                    ------- `func` moved due to this call
118 |     println!("{}", func(4));
    |                    ^^^^ value used here after move
    |
note: this value implements `FnOnce`, which causes it to be moved when called
   --> src/main.rs:117:20
    |
117 |     println!("{}", func(3));
    |                    ^^^^
help: consider further restricting this bound
    |
115 |     F: FnOnce(usize) -> bool + Copy,
    |                              ++++++

根据报错信息可知,FnOnce 的闭包在调用后会被 move 掉,因此我们不再能调用它了。但是如果加上 Copy 的 trait bound,就能可以多次调用这个 func 。查询 GPT,得知如果一个闭包捕获的变量都是 Copy 的,那么它也是 Copy 。这两个性质都能体现出闭包本身就是一种 rust 类型,一个类型的变量被 move 掉以后自然不能再访问,而如果一个类型里拥有的引用都是 Copy 的,那么它自然也是 Copy 的。

1.4. 补充之二:闭包的底层原理

这部分资料来源于 Efficient Rust p14。闭包的实现原理是,编译期创建一个隐藏类,存放所有需要被闭包捕获的引用,然后将闭包的匿名函数体实现为此类的方法。

2. Send Sync

3. Sized ?Sized

4. Copy Clone

5. Sink Stream

6. Ref RefCell

知识共享许可协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇