From 1429899531d706a2b7df23ba623d56bde1945fc4 Mon Sep 17 00:00:00 2001 From: infopensource <44309311+infopensource@users.noreply.github.com> Date: Thu, 13 Jul 2023 03:17:54 +0800 Subject: [PATCH 1/3] add some details about FnMut The example from original confused many of us, I added some explainations to explain what the example actually did. --- src/advance/functional-programing/closure.md | 44 +++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/advance/functional-programing/closure.md b/src/advance/functional-programing/closure.md index 81aeffc8..781556ba 100644 --- a/src/advance/functional-programing/closure.md +++ b/src/advance/functional-programing/closure.md @@ -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); +``` + +这段代码非常清晰的说明了 `update_string` 实现了 `FnMut` 特征。 + +不是说需要用mut关键词修饰才能将闭包声明为可变类型吗?事实上,`FnMut`只是trait的名字,声明`FnMut`的变量和要不要mut没啥关系,`FnMut`是推导出的trait类型,`mut`是rust语言层面的一个修饰符,用于声明一个绑定是可变的。Rust从trait类型系统和语言修饰符两方面保障了我们的程序正确运行。 + +我们在使用`FnMut`类型闭包时需要捕获外界的可变借用,因此我们常常用搭配`mut`修饰符使用。但我们要始终记住,二者是相互独立的。 + +因此,让我们再回头分析一下这段代码:在`main`函数中,首先创建了一个可变的字符串`s`,然后定义了一个可变类型闭包`update_string`,该闭包接受一个字符串参数并将其追加到`s`中。接下来调用了`exec`函数,并将`update_string`闭包的所有权移交给它。最后打印出了字符串`s`的内容。 + +细心的读者可能已经注意到了,在我们对代码的分析中提到`update_string`闭包的所有权被移交给了`exec`函数。这说明`update_string`没有实现copy trait,但并不是所有闭包都没有实现copy trait,闭包是否会实现copy trait,总结一下就是,只要闭包捕获的类型都实现`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)`: From 494575c6c875b66e7f4afe5f75ccc2e9168e3008 Mon Sep 17 00:00:00 2001 From: infopensource <44309311+infopensource@users.noreply.github.com> Date: Thu, 13 Jul 2023 10:46:09 +0800 Subject: [PATCH 2/3] fix some grammarly mistakes --- src/advance/functional-programing/closure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/advance/functional-programing/closure.md b/src/advance/functional-programing/closure.md index 781556ba..853d1898 100644 --- a/src/advance/functional-programing/closure.md +++ b/src/advance/functional-programing/closure.md @@ -490,7 +490,7 @@ fn exec<'a, F: FnMut(&'a str)>(mut f: F) { } ``` -我们注意到这段代码中`update_string`没有使用mut关键字修饰,而上文我们提到想要在闭包内部捕获可变借用,需要用关键词把该闭包声明为可变类型。我们确实这么做了————`exec(mut f: F)`表明我们的`exec`接收的是一个可变类型的闭包。看似这段代码中`update_string`声明为不可变闭包,但是`exec(mut f: F)`函数接收的又是可变参数,为什么可以正常执行呢? +我们注意到这段代码中`update_string`没有使用mut关键字修饰,而上文我们提到想要在闭包内部捕获可变借用,需要用关键词把该闭包声明为可变类型。我们确实这么做了———`exec(mut f: F)`表明我们的`exec`接收的是一个可变类型的闭包。看似这段代码中`update_string`声明为不可变闭包,但是`exec(mut f: F)`函数接收的又是可变参数,为什么可以正常执行呢? 是的,rust不可能接受类型不匹配的形参和实参通过编译,这说明`update_string`一定是一个可变类型的闭包,我们不妨看看rust-analyzer给出的类型标注: From ea00e8390f7d40ae03550c96ebf390d681ad16e2 Mon Sep 17 00:00:00 2001 From: hacktor <44309311+infopensource@users.noreply.github.com> Date: Wed, 19 Jul 2023 18:52:38 +0800 Subject: [PATCH 3/3] Update closure.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改了行文,更加流畅易于阅读 --- src/advance/functional-programing/closure.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/advance/functional-programing/closure.md b/src/advance/functional-programing/closure.md index 853d1898..720e25f8 100644 --- a/src/advance/functional-programing/closure.md +++ b/src/advance/functional-programing/closure.md @@ -490,9 +490,9 @@ fn exec<'a, F: FnMut(&'a str)>(mut f: F) { } ``` -我们注意到这段代码中`update_string`没有使用mut关键字修饰,而上文我们提到想要在闭包内部捕获可变借用,需要用关键词把该闭包声明为可变类型。我们确实这么做了———`exec(mut f: F)`表明我们的`exec`接收的是一个可变类型的闭包。看似这段代码中`update_string`声明为不可变闭包,但是`exec(mut f: F)`函数接收的又是可变参数,为什么可以正常执行呢? +这段代码中`update_string`没有使用mut关键字修饰,而上文提到想要在闭包内部捕获可变借用,需要用关键词把该闭包声明为可变类型。我们确实这么做了———`exec(mut f: F)`表明我们的`exec`接收的是一个可变类型的闭包。这段代码中`update_string`看似被声明为不可变闭包,但是`exec(mut f: F)`函数接收的又是可变参数,为什么可以正常执行呢? -是的,rust不可能接受类型不匹配的形参和实参通过编译,这说明`update_string`一定是一个可变类型的闭包,我们不妨看看rust-analyzer给出的类型标注: +rust不可能接受类型不匹配的形参和实参通过编译,我们提供的实参又是可变的,这说明`update_string`一定是一个可变类型的闭包,我们不妨看看rust-analyzer自动给出的类型标注: ```rust let mut s: String = String::new(); @@ -500,15 +500,15 @@ fn exec<'a, F: FnMut(&'a str)>(mut f: F) { let update_string: impl FnMut(&str) = |str| s.push_str(str); ``` -这段代码非常清晰的说明了 `update_string` 实现了 `FnMut` 特征。 +rust-analyzer给出的类型标注非常清晰的说明了 `update_string` 实现了 `FnMut` 特征。 -不是说需要用mut关键词修饰才能将闭包声明为可变类型吗?事实上,`FnMut`只是trait的名字,声明`FnMut`的变量和要不要mut没啥关系,`FnMut`是推导出的trait类型,`mut`是rust语言层面的一个修饰符,用于声明一个绑定是可变的。Rust从trait类型系统和语言修饰符两方面保障了我们的程序正确运行。 +为什么`update_string`没有用`mut`修饰却是一个可变类型的闭包?事实上,`FnMut`只是trait的名字,声明变量为`FnMut`和要不要mut没啥关系,`FnMut`是推导出的特征类型,`mut`是rust语言层面的一个修饰符,用于声明一个绑定是可变的。Rust从特征类型系统和语言修饰符两方面保障了我们的程序正确运行。 -我们在使用`FnMut`类型闭包时需要捕获外界的可变借用,因此我们常常用搭配`mut`修饰符使用。但我们要始终记住,二者是相互独立的。 +我们在使用`FnMut`类型闭包时需要捕获外界的可变借用,因此我们常常搭配`mut`修饰符使用。但我们要始终记住,二者是相互独立的。 因此,让我们再回头分析一下这段代码:在`main`函数中,首先创建了一个可变的字符串`s`,然后定义了一个可变类型闭包`update_string`,该闭包接受一个字符串参数并将其追加到`s`中。接下来调用了`exec`函数,并将`update_string`闭包的所有权移交给它。最后打印出了字符串`s`的内容。 -细心的读者可能已经注意到了,在我们对代码的分析中提到`update_string`闭包的所有权被移交给了`exec`函数。这说明`update_string`没有实现copy trait,但并不是所有闭包都没有实现copy trait,闭包是否会实现copy trait,总结一下就是,只要闭包捕获的类型都实现`Copy`的话,这个闭包就默认实现了`Copy`。 +细心的读者可能注意到,我们在上文的分析中提到`update_string`闭包的所有权被移交给了`exec`函数。这说明`update_string`没有实现`Copy`特征,但并不是所有闭包都没有实现`Copy`特征,闭包自动实现`Copy`特征的规则是,只要闭包捕获的类型都实现了`Copy`特征的话,这个闭包就会默认实现`Copy`特征。 我们来看一个例子: @@ -517,7 +517,7 @@ let s = String::new(); let update_string = || println!("{}",s); ``` -这里拿到的是`s`的不可变引用,所以是能`Copy`的。而如果拿到的是`s`的所有权或可变引用,都是不能`Copy`的。我们刚刚的代码就属于第二类,拿到的是`s`的可变引用,是没有实现`Copy`的。 +这里取得的是`s`的不可变引用,所以是能`Copy`的。而如果拿到的是`s`的所有权或可变引用,都是不能`Copy`的。我们刚刚的代码就属于第二类,取得的是`s`的可变引用,没有实现`Copy`。 ```rust // 拿所有权