|
|
|
@ -490,7 +490,49 @@ fn exec<'a, F: FnMut(&'a str)>(mut f: F) {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
这段代码非常清晰的说明了 `update_string` 实现了 `FnMut` 特征
|
|
|
|
|
这段代码中`update_string`没有使用mut关键字修饰,而上文提到想要在闭包内部捕获可变借用,需要用关键词把该闭包声明为可变类型。我们确实这么做了———`exec(mut f: F)`表明我们的`exec`接收的是一个可变类型的闭包。这段代码中`update_string`看似被声明为不可变闭包,但是`exec(mut f: F)`函数接收的又是可变参数,为什么可以正常执行呢?
|
|
|
|
|
|
|
|
|
|
rust不可能接受类型不匹配的形参和实参通过编译,我们提供的实参又是可变的,这说明`update_string`一定是一个可变类型的闭包,我们不妨看看rust-analyzer自动给出的类型标注:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
let mut s: String = String::new();
|
|
|
|
|
|
|
|
|
|
let update_string: impl FnMut(&str) = |str| s.push_str(str);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
rust-analyzer给出的类型标注非常清晰的说明了 `update_string` 实现了 `FnMut` 特征。
|
|
|
|
|
|
|
|
|
|
为什么`update_string`没有用`mut`修饰却是一个可变类型的闭包?事实上,`FnMut`只是trait的名字,声明变量为`FnMut`和要不要mut没啥关系,`FnMut`是推导出的特征类型,`mut`是rust语言层面的一个修饰符,用于声明一个绑定是可变的。Rust从特征类型系统和语言修饰符两方面保障了我们的程序正确运行。
|
|
|
|
|
|
|
|
|
|
我们在使用`FnMut`类型闭包时需要捕获外界的可变借用,因此我们常常搭配`mut`修饰符使用。但我们要始终记住,二者是相互独立的。
|
|
|
|
|
|
|
|
|
|
因此,让我们再回头分析一下这段代码:在`main`函数中,首先创建了一个可变的字符串`s`,然后定义了一个可变类型闭包`update_string`,该闭包接受一个字符串参数并将其追加到`s`中。接下来调用了`exec`函数,并将`update_string`闭包的所有权移交给它。最后打印出了字符串`s`的内容。
|
|
|
|
|
|
|
|
|
|
细心的读者可能注意到,我们在上文的分析中提到`update_string`闭包的所有权被移交给了`exec`函数。这说明`update_string`没有实现`Copy`特征,但并不是所有闭包都没有实现`Copy`特征,闭包自动实现`Copy`特征的规则是,只要闭包捕获的类型都实现了`Copy`特征的话,这个闭包就会默认实现`Copy`特征。
|
|
|
|
|
|
|
|
|
|
我们来看一个例子:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
let s = String::new();
|
|
|
|
|
let update_string = || println!("{}",s);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
这里取得的是`s`的不可变引用,所以是能`Copy`的。而如果拿到的是`s`的所有权或可变引用,都是不能`Copy`的。我们刚刚的代码就属于第二类,取得的是`s`的可变引用,没有实现`Copy`。
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
// 拿所有权
|
|
|
|
|
let s = String::new();
|
|
|
|
|
let update_string = move || println!("{}", s);
|
|
|
|
|
|
|
|
|
|
exec(update_string);
|
|
|
|
|
// exec2(update_string); // 不能再用了
|
|
|
|
|
|
|
|
|
|
// 可变引用
|
|
|
|
|
let mut s = String::new();
|
|
|
|
|
let mut update_string = || s.push_str("hello");
|
|
|
|
|
exec(update_string);
|
|
|
|
|
// exec1(update_string); // 不能再用了
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
3. `Fn` 特征,它以不可变借用的方式捕获环境中的值
|
|
|
|
|
让我们把上面的代码中 `exec` 的 `F` 泛型参数类型修改为 `Fn(&'a str)`:
|
|
|
|
|