以下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
是不可变的?
答案 0 :(得分:8)
默认情况下,所有变量不可变。您必须explicitly tell the compiler which variables are mutable let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3);
关键字:
&mut self
Vec::push
被定义为需要对向量(fn push(&mut self, value: T)
)的可变引用:
fn push(&mut Vec<T>, value: T)
这使用method syntax,但在概念上与:
相同Python 3.6.1 |Anaconda 4.4.0 (x86_64)| (default, May 11 2017, 13:04:09)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
我强烈建议您阅读The Rust Programming Language, second edition。它涵盖了这个初学者问题以及您将要遇到的许多其他初学者问题。
答案 1 :(得分: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.push
是不可变的,则无法致电v
,因为push
期望&mut self
。
这可能是限制性的,因此Rust提供的工具可以让您微调此行为,以准确编码您需要的安全保证。如果您想获得接近C#行为的东西,可以使用RefCell
包装器(或其他几种包装器类型之一)。 RefCell<Vec<T>>
本身不必是可变的,因为函数能够解包它并修改里面的Vec
。