在变量名前面和":"后面放置"mut"有什么区别?

137

以下是我在 Rust 文档中看到的两个函数签名:

fn modify_foo(mut foo: Box<i32>) { *foo += 1; *foo }
fn modify_foo(foo: &mut i32) { *foo += 1; *foo }

为什么 mut 放置位置不同?

似乎第一个函数也可以声明为

fn modify_foo(foo: mut Box<i32>) { /* ... */ }

4
对于C++程序员来说,这个区别类似于指针const和指向对象的指针const之间的区别。 - legends2k
4个回答

169

如果您来自C/C++,那么基本上可以这样理解:

// Rust          C/C++
    a: &T     == const T* const a; // can't mutate either
mut a: &T     == const T* a;       // can't mutate what is pointed to
    a: &mut T == T* const a;       // can't mutate pointer
mut a: &mut T == T* a;             // can mutate both

您会注意到这两者是相反的。C/C++采用“黑名单”方法,如果您想要某些东西不可变,则必须明确声明,而Rust采用“白名单”方法,如果您想要某些东西可变,则必须明确声明。


5
这是一张很棒的表格。值得注意的是,&mut T 引用也类似于 C 语言中的 T* restrict 指针:它们可能不会被别名引用。而 &T 引用没有此限制,并且没有与未带有 restrict 限定符的 T* 指针相对应的引用类型。 - trent
2
我没有C语言背景,但我仍然认为这个解释更好(带有注释),而不是被接受的答案,有时候简单比冗长更好。 - Kresten
对于那些忘记了C/C++规则的人 - nbro

139

mut foo: T 表示你有一个名为 foo 的变量,它是一个类型为 T 的变量。你可以改变这个变量所引用的内容:

let mut val1 = 2;
val1 = 3; // OK

let val2 = 2;
val2 = 3; // error: re-assignment of immutable variable

这也使您能够修改您拥有的结构体字段:
struct Monster { health: u8 }

let mut orc = Monster { health: 93 };
orc.health -= 54;

let goblin = Monster { health: 28 };
goblin.health += 10; // error: cannot assign to immutable field

foo: &mut T表示您有一个变量引用(&)一个值,并且您允许更改(mut)所引用的值(包括字段,如果它是一个结构体):

let val1 = &mut 2;
*val1 = 3; // OK

let val2 = &2;
*val2 = 3; // error: cannot assign to immutable borrowed content

请注意,&mut 只能用于引用类型 - foo: mut T 不是有效的语法。当有意义时,您也可以组合这两个限定符(let mut a: &mut T)。

16
我明白了。我猜这就像在C++中,你可以有 int const*int *const 来实现不同的功能。 - Jimmy Lu
4
@Shepmaster 你可能想要添加一个 mut 绑定,这样就可以在结构体内部进行变异(如果它是一个结构体)。 - Scott Olson
10
不要把&mut Type看作是&(mut Type),而应该看作是(&mut) Type。关键字mut通常不用于类型中,但是有一种叫做&mut的引用类型。 - Scott Olson
3
所以,您的意思是 &mut 只是为了避免引入新关键字而使用的方便记法,但实际上它和左侧通常的 mut 关键字是不同的概念? - didierc
3
是的。您可以将&T&mut T视为sugar(我刚刚想出来的类型)的Ref<T>RefMut<T>。请注意,这并不改变原始含义。 - Scott Olson
显示剩余4条评论

39

以下自然语言翻译似乎帮我搞清楚了一些事情...

let x = value;
  x {binds immutably} to {immutable value}

let mut x = value;
  x {binds mutably} to {possibly mutable value}

let x = &value;
  x {binds immutably} to {a reference to} {immutable value}

let x = &mut value;
  x {binds immutably} to {a reference to} {mutable value}

let mut x = &value;
  x {binds mutably} to {a reference to} {immutable value}

let mut x = &mut value;
  x {binds mutably} to {a reference to} {mutable value}

什么是可变绑定和可变值:

  • {binds mutably} 表示该绑定可以被重新赋值
  • {mutable value} 表示该值的内容可以更改
  • 要能够 改变 值,需要同时拥有 可变绑定可变值

注意:

参考可变性 vs 对象可变性

x 这样的引用变量,在 let x = &mut y 中,它是一个与其指向的目标变量 y 分离的变量。特别地,x 有自己在堆栈中的位置和可变性权限。因此,如果像这里一样,x 是不可变的,则不能将其重新赋值为指向其他变量的指针。该限制与通过它来改变目标的能力无关,如 *x = some_value;目标是具有自己的可变性权限的独立变量。然而,如果像 let mut w = &mut p 这样,w 是可变的,则确实可以将其重新赋值为指向其他类型相似的变量:w = &mut z


你确定语句"To be able to mutate a value you need both a mutable binding and a mutable value"是正确的吗?对我来说,以下代码是可以运行成功的:fn main() { let mut value = 4; println!("{}", value); let x = &mut value; println!("{}", x); *x = 5; println!("{}", x); } - user782220
value 是一个位置。该位置的内容是可变的。它当前包含 4x 是堆栈上的位置。该位置的内容是不可变的。它当前包含对 value 的地址。地址类型允许修改该位置内容。因此,虽然您无法更改 x,但可以更改其所指向的内容 *x。这些限制在 safe rust 中在编译时强制执行,防止某些代码使用变量 valuex 有可变引用时不可用:同一时间只能有一个可变引用变量。 - George
但我的意思是 $x$ 不是一个可变的绑定。 - user782220
我明白你的意思。引用并不是对目标的绑定。引用是一个独立的变量,具有自己的可变性权限。具体来说,let x = &mut y 创建了一个不可变指针变量 x,位于堆栈上,并包含变量 y 的地址,在这种情况下,该位置是可变的。另一方面,let mut x = &mut y 是一个可变指针。特别地,重新赋值 x = &mut z 现在是可能的,而之前则不行。 - George

1

可变引用类型变量

当您拥有一个

let mut x: &T = value;

这意味着x是一个变量,它“引用”T的一个实例,就好像将T实例的内存地址存储在x的值中一样。这个“引用”(即“内存地址”)是这种情况下可变性的主题:可以通过以下方式修改x以引用不同的T实例:
x = some_other_T_instance;

但是x无法通过赋值改变引用(即T实例的值):

// Illegal
*x = a_different_value;

不可变的可变引用类型变量

当你拥有一个

let x: &mut T = value;

这意味着x是一个变量,它引用了一个可变的T实例。在这种情况下,引用对象(即实际值)是可变性的主题。可以通过引用像这样修改它。
*x = some_other_value;

但是参考本身(即变量 x 中的“内存地址”)无法:

// illegal
x = a_different_value;

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