为什么函数参数提示《remove this mut》
翻译自《https://www.snoyman.com/blog/2020/05/no-mutable-parameters-in-rust》
在回顾Begin Rust书中的倒数第二章时,出现了一个稍微高级的话题。这个话题引起了我一段时间的兴趣,特别是因为它展示了Rust和Haskell如何处理可变性的一些根本差异。该主题对于本书而言太高了,但是我想提供一个外部资源,以供有好奇心的人参考。就是这里!
让我们逐步构建它。以下程序可以编译吗?
1 |
|
答:不!x是一个不可变的变量,因此不能+= 1在其上使用。解决这个问题很容易:add mut:
1 |
|
但是由于加1和打印在我的应用程序中是如此重要(是的,这很讽刺),所以我决定将其提取为自己的函数。告诉我,这段代码可以编译吗?
1 |
|
不,并且出于与第一个示例相同的原因:x是不可变的。修复也很容易:
1 |
|
现在,我们的函数add_and_print
拥有唯一的参数x,它的类型是i32,并且是可变的。嗯不错。最后,我们可以在 main
中调用此函数。告诉我,该程序可以编译并运行吗?是否和预想的一样没有任何警告吗?
1 |
|
答:它会编译,运行并生成输出x == 6。但是,它确实有一个警告:
1 |
|
最初,至少对于我来说,这确实令人惊讶。add_and_prin
t需要接收一个可变变量i32作为其第一个参数。我们为其提供了可变的i32。然后编译器说mut
在 main
中是不必要的。这是什么情况?
我上面的解释有一个错误。函数add_and_print
,可能非常令人困惑,不用将可变变量i32作为参数。“但是前面说需要mut的!!!”,而现在又说mut是不必要的。 确实如此。mut详细信息位于函数内部,而不是其类型签名的一部分。这听起来令人困惑,所以让我解释一下。
在Rust中有一个模式,我称之为rust的3条规则。这涵盖了以下事实:在许多情况下,我们最终会得到三个“版本”的事物:
1 |
|
这可以应用于函数参数,例如:
1 |
|
请注意,差异完全在冒号之后。冒号后的内容是3个不同的类型
,而类型
构成了函数签名
。
但是,示例中的x
冒号前的内容对函数签名
没有影响。变量名称
与函数的签名无关。一旦冒号右边传递一个值
函数即可决定要调用的内容。
此规则不仅适用于变量名称。也适用于可变变量
。mutable
是rust中变量
的一个feature而不是值
的feature。当使用let mut x = 5时,表示的是 “创建一个叫x的变量
,它指向一个值
5,并且允许用x来对该值
进行改变。” 如果没有mut,将不再被允许通过变量
x对该值
进行改变。
您可能会有一个直觉的反应,如果您无法修改变量
,那你就只能只读它了。一般直觉应该就是这样。但Rust中并非如此。您还可以做另一件事:将值
移到另一个作用域。add_and_print
通过move
接受值
,即使x是一个不可变的变量
,我仍然可以移动它所指向的值
。
一旦移动了值
,那就完全取决于add_and_print
如何处理它。即使原始变量
是不可变的,也可以将其变为可变的。这是因为在函数调用中值
本身被传递过来,而不是变量。而值可以根据需要来变为可变的。
因此,该程序的无警告版本为:
1 |
|
实际上,即使没有函数调用,move后为可变的也是可能发生的。例如,您可以在单个函数中将不可变变量“升级”为可变变量:
1 |
|
“等一下” 您抱怨道,“你不能将不可变的引用“升级”为可变的引用!” ,对于我的解释或许还有其他类似的关于抱怨。我在这里稍加修饰的是: 当涉及到引用时,可变性被转化为值的可变性。那是因为对于类似x: &i32这种形式的引用,x自身并没有任何数值,它引用
了一个数字。对于可变或不可变是对引用
本身来说的,因为引用
自身是一种类型。因此,您不能简单地将不可变引用升级为可变引用。此代码已损坏:
1 |
|
因此,总结一下:
- 您可以拥有
值
,或值
的不变引用
或值
的可变引用
值
只有2种状态,要么可变的,要么不可变- 相似的
变量
也只有2种状态,要么可变的,要么不可变 - 当将
值
移动到新变量
中(通过 let或函数调用)时,可以更改变量
的可变性 - 函数签名中
变量
的可变性和名称并不影响函数签名 - 引用的可变性内置在类型本身中,因此您不能将不可变的引用“升级”为可变的引用
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!