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
。
以下 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
。