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

3
以下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 是不可变的?

2个回答

9

所有变量默认情况下都是不可变的。你必须通过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)

这里使用方法语法,但它在概念上与以下代码相同:

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

强烈推荐您阅读Rust编程语言第二版。它涵盖了这个初学者问题以及您将遇到的许多其他初学者问题。


3
在 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

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