我知道如何使代码工作,我只是想知道为什么会这样。
假设下面的程序:
fn dummy(name: String) {
let last_name = " Wang".to_string();
name.push_str(&last_name);
println!("Hello, {}", name);
}
fn main() {
println!("What is your name?");
let mut name = String::new();
std::io::stdin().read_line(&mut name).expect("Couldn't read input!");
name.pop();
dummy(name);
}
当试图编译它时,出现以下错误:
error[E0596]: cannot borrow `name` as mutable, as it is not declared as mutable
--> print.rs:3:5
|
1 | fn dummy(name: String) {
| ---- help: consider changing this to be mutable: `mut name`
2 | let last_name = " Wang".to_string();
3 | name.push_str(&last_name);
| ^^^^ cannot borrow as mutable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0596`.
我知道只是在函数定义中添加mut
旁边的name
解决了这个问题,但是为什么需要在函数定义中声明它是可变的,当变量name
在main
函数中被定义为可变的呢?
难道编译器不知道变量之前是可变的吗?为什么它不能转移所有权和可变的属性?和它一起吗?
也许这是一个愚蠢的问题,但我是Rust的新手。如果是这样的话,会不会带来一些新的问题/bug ?如果有,能否举例说明?name
作为参数传递的事实只是一个细节。在这个简化的例子中,我们可以重现同样的效果。
fn main() {
let mut name1 = "first".to_owned();
name1.push_str(" second");
let mut name2 = name1;
name2.push_str(" third");
let name3 = name2;
// name3.push_str(" fourth"); // rejected
let mut name4 = name3;
name4.push_str(" fifth");
println!("{}", name4);
}
字符串的所有权从name1
变为name2
,name3
然后name4
,每个变量(绑定)决定(有或没有mut
)是否可以改变字符串,它现在是唯一的所有者。
初始化函数的形参类似于初始化另一个变量(借用/所有权转移/复制…),一旦进入函数,形参就会被视为在此上下文中可能可变或不可变的任何其他局部变量。如果你想修改这个参数,你可以用mut
声明它,或者把它转移到另一个用mut
声明的局部变量。
注意我们处理的是值这里,不是参考文献。当然,您不能从&T
初始化&mut T
。但是在引用前加上mut
(如mut &T
或mut &mut T
)提供了将该引用重新分配给另一个值(被认为是可变的,取决于正确的mut
)的能力。如果您熟悉C或c++,这类似于在声明指针时在星号之前或之后(或两侧)使用const
。
简而言之,在变量上使用mut
是相对于您的意图修改算法中存储在该变量中的内容,但它不是该变量内容的属性。
除了@prog-fh的答案之外,Rust的一个重要特性是它允许本地推理。换句话说,当您查看定义时,您应该拥有所需的所有信息,以确定代码是否正确以及它可能做什么或不做什么。在您的情况下,这意味着如何调用dummy
以及传递什么参数无关紧要,它的定义必须能够独立存在。所以你应该能够知道所有关于它的信息,只要看看这部分代码:
fn dummy(name: String) {
let last_name = " Wang".to_string();
name.push_str(&last_name);
println!("Hello, {}", name);
}
当使用其他人编写的函数时,这使得作为一个团队编写代码更容易,或者当您可能不记得最初编写代码时的所有细节时,在长期运行中维护代码更容易。