为什么在引用可变变量时,Rust需要重新声明可变性?

3
我正在阅读《Rust编程之道》的第二章,其中有一些我不理解的内容吸引了我的注意:
use std::io;

fn main() {
    println!("Guess the number!");

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess)
        .expect("Failed to read line");

    println!("You guessed: {}", guess);
}

在代码的第5行,它声明了一个可变变量,使用let mut guess = String::new(),但是在下一行中,read_line()的参数也有mut关键字。
如果变量在一开始就被定义为可变的,那么为什么我们要再次使用mut而不是像这样使用引用:
io::stdin().read_line(&guess).expect("Failed to read line");

如果变量定义了类型,那么当我们使用引用时,类型(mut)默认存在吗?

5
你应该继续阅读 :) 现在你只需知道,和变量一样,默认情况下引用是不可变的。因此,为了让它可变,你需要写成&mut guess而不是&guess。(第4章会更彻底地解释引用。) - HFBrowning
是的,我看过那个了,但我不想跳过这个课程,我每天读一页,现在如果不理解会打乱我的节奏...那个文档一直很详细,直到那个点 :) ... - samayo
3个回答

7

TL;DR: 这是一项设计决策。Rust编译器可以合理地推断是否需要可变性;但对于人类读者来说,这可能不太明显。


长篇故事

如果你看一下Rust的前辈,你会发现在C++中使用引用参数并不被普遍赞赏。在C++中:

foo.call(bar);

只有call的定义才能让您知道bar是按值传递、按const引用传递还是按可变引用传递。因此,Google风格指南以强制使用传递指针的方式来区分在调用端是否可以修改变量而臭名昭著。

在设计Rust时,明确性是一个重点。理由是代码被阅读的次数比编写的次数多,因此语法和语义应该被优化为易于阅读和理解。

明确性和简洁性之间存在着一种紧张关系,因此并不总是首选明确性,但通常是首选的。

在可变引用的情况下,考虑到借用检查规则和可变借用对其产生的影响,选择了明确性。


5

第一和第二行代码怎么解释?如果 foo 是可变类型,那它不应该在整个过程中保持这种状态吗?感谢您的回答。 - samayo
@ANW foo 的类型是 i32 — 可变性不是该类型的属性。example 函数的参数类型是 &i32,而 example2 函数的参数类型是 &mut i32。仅仅因为某个变量是可变的,并不意味着你总是想要改变它。知道某些东西不能被改变更加有力。 - Shepmaster
2
@ANW 实际上在这里起作用的类型并不是 foo 的类型,而是 example1example2 参数的类型。由于编译器知道 example1 接受一个 &i32 参数,而 example2 接受一个 &mut i32,因此函数调用中的 mut 对于编译器来说确实是多余的。然而,对于阅读代码的程序员来说,它是有用的,因为他们可以一眼看出函数调用是否会改变参数。记住,你花在阅读代码上的时间比写代码的时间要多得多。 - Jmb

3
请记住,在Rust中默认情况下所有内容都是不可变的。当您使用&创建对某个东西的引用时,默认情况下会创建对某个不可变事物的引用,至少对于该引用而言,值本身可以是可变的,值的实际可变状态并不重要。
当您从所有内容都是可变的语言转换而来时,这有点违反直觉。您无需显式告诉某些内容是可变的,因为这是默认行为。几乎不存在需要在创建时显式编写对某些内容不可变的引用的情况。
因此,要创建对某些可变内容的引用,必须显式使用&mut。这是一条规则,编译器知道该值可以被修改并可能为您执行此操作,但Rust要求您显式编写它,就这么简单。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接