Rust 如何知道变量是否可变?

How can Rust know if a variable is mutable?

以下 C# 代码可以正常编译:

static readonly List<int> list = new List<int>();
static void Main(string[] args)
{
    list.Add(1);
    list.Add(2);
    list.Add(3);
}

如果我用 Rust 写类似的代码,它不会编译,因为它不能借用不可变的 v 作为可变的:

let v = Vec::new();
v.push(1);
v.push(2);
v.push(3);

push 函数如何知道 v 是不可变的?

默认情况下,所有变量不可变。您必须 explicitly tell the compiler which variables are mutable 通过 mut 关键字:

let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3);

Vec::push 定义为需要对向量 (&mut self) 的可变引用:

fn push(&mut self, value: T)

这使用 method syntax,但在概念上与:

fn push(&mut Vec<T>, value: T)

强烈推荐您阅读The Rust Programming Language, second edition。它涵盖了这个初学者问题以及您将遇到的许多其他初学者问题。

在 Rust 中,默认情况下绑定是不可变的。所以你可能认为以下是等价的:

readonly List<int> list = new List<int>(); // C#
let list = Vec::new();                     // Rust

还有这些:

List<int> list = new List<int>(); // C#
let mut list = Vec::new();        // Rust

但是,正如您所发现的,情况并非如此。

在 C# 版本的 Add 方法中,没有关于您用来调用它的绑定的信息。 Add 方法无法声明它会改变其数据,因此 C# 编译器无法阻止您传递对 readonly 绑定的引用。 readonly 关键字可防止您用全新的 List 覆盖 list 绑定,但它不会阻止您更改现有数据中的数据。 C# 阻止您更改 readonly 绑定的值,但在这种情况下该值是指向您的数据的指针,而不是数据本身。

在 Rust 中,如果一个方法需要改变底层数据,它必须将它的第一个参数声明为 self&mut self.

self的情况下,则数据移动到方法中,不能再使用原来的绑定。该方法是否更改数据并不重要,因为调用者不能再使用该绑定。

在可变引用的情况下,&mut self,如果原始绑定也是可变的,Rust 只会让您创建它。如果原始绑定是不可变的,那么这将产生编译错误。如果 v 是不可变的,就不可能调用 v.push,因为 push 期望 &mut self.

这可能是有限制的,因此 Rust 提供了一些工具,让您可以微调此行为以准确编码您需要的安全保证。如果您想获得接近 C# 行为的东西,您可以使用 RefCell 包装器(或其他几种包装器类型之一)。 RefCell<Vec<T>> 本身不必是可变的,函数就可以打开它并修改里面的 Vec