当一个不可变的结构体被移到向量中时为什么会变成可变的?

4

我刚开始学习 Rust,正在了解该语言的可变性规则。我试图弄清楚结构体、对象和数组是否是深度不可变的。

所以我有这个结构体:

struct Foo {
    bar: String,
}

我希望能够了解关于深层可变性的规则。在创建结构体时,必须将其定义为mut,才能对其属性进行更改。

let mut f = Foo{ 
    bar: String::from("hello") 
};

f.bar = String::from("foo"); // Must be mut to change a property
println!("{:?}", f.bar);

然而,当我定义一个不可变结构体并将其移入可变向量中时,该结构体就变成了可变的。

let f = Foo{ 
    bar: String::from("hello") 
};

let mut strings: Vec<Foo> = vec![f];

strings[0].bar = String::from("foo");

println!("{:?}", strings[0].bar);

我猜测 f 被复制到了可变向量中(并从其作用域中删除了?),然后将该副本重新分配为可变的,但我并不确定。

我能否获得一些关于发生了什么的见解?

1个回答

12

mut不是值的属性,它是绑定的属性(我特别指的是不可变引用而非可变引用,即只有let mut)。

let v的意思是“我不会尝试通过v直接或间接地更改它包含的值”,

但是一旦它不再在v中(即它已移动),更改它就没问题了。

因此,以下代码可以成功编译:

let v = Foo { bar: String::new() };
let mut mutable_v = v;
mutable_v.bar = "abc".to_owned();
自从我们将值从v中移出,v的可变性就不再相关了。
同样地,自从我们将值从f中移出并放入向量strings中,f的可变性就不再重要了,但strings的可变性很重要,因为我们不通过f而是通过strings改变这个值。

谢谢,这解释了一切。在我的脑海中,我将绑定和值耦合在一起 - 假设该值由绑定定义为不可变的。问题:可变借用&mut允许对变量进行临时可变访问,原始变量必须标记为mut才能有资格进行多个借用,是吗? - David Alsh
@DavidAlsh 正确。但是可变借用更加复杂:它们确实涉及某个层面上的值。 - Chayim Friedman
我还要补充一点,这也是 Rust 不需要像许多高级语言(如 C#、Java、JS 等)那样在类型层面上需要不可变性的原因之一,因为你需要更加小心地保护它们。在这些语言中,如果你有一个对象的句柄,你可以改变它,因为不存在不可变句柄和可变句柄之分(除非使用接口,但绕过接口很容易)。在 Rust 中,你可以给某个东西赋予不可变的访问权限,并且知道(除了未定义行为),该值不能被该东西更改。 - cdhowie
(为公平起见,您也可以使用C++和const引用。Rust在这方面要严格得多——在C++中,除非引用的参考对象被声明为const,否则去除引用的const属性是定义行为,但在Rust中,如果没有使用“unsafe”关键字,就不能这样做。) - cdhowie
1
在Rust中,将共享引用转换为可变引用是立即UB。但正如我所说,引用更加复杂。变量的可变性基本上是一个lint,与值无关。然而,引用的可变性是抽象机器上的真实问题。 - Chayim Friedman

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