可变性

欢马劈雪     最近更新时间:2020-08-04 05:37:59

93

可变性,可以改变东西的能力,与其他语言相比它在 Rust 中有点不同。可变性的第一个方面是它的非默认状态:

let x = 5;
x = 6; // error!

我们可以应用 mut 关键字来介绍可变性:

let mut x = 5;

x = 6; // no problem!

这是一个可变的变量绑定。当一个绑定是可变时,这意味着你可以更改绑定的指向。所以在上面的例子中,不是 x 的值发生变化,而是这个绑定从 i32 更改为其他。

如果你想要更改绑定的指向,你将需要一个可变的引用:

let mut x = 5;
let y = &mut x;

y 是一个绑定到一个可变引用的不可变值,这意味着你不可以将 y 绑定到其他 ( y = &mut z ),但是你可以改变绑定到 y上的东西 ( *y=5 ).细微的差别。

当然,如果你两个都需要,可以如下书写:

let mut x = 5;
let mut y = &mut x;

现在 y 可以绑定到另一个值,并且这个引用的值可以改变。

注意到 mut 是<模式的一部分是非常重要的,你可以这样编写代码:

let (mut x, y) = (5, 6);

fn foo(mut x: i32) {

内部和外部可变性

然而,在 Rust 中当我们说有些东西是‘不变’的,这并不意味着它不可以更改:我们说的是它有‘外部可变性’。考虑如下例子,Arc<T>:

use std::sync::Arc;

let x = Arc::new(5);
let y = x.clone();

当我们调用 clone() 时,Arc<T> 需要更新引用计数。然而我们在这里还没有使用 mutx 是一个不可变绑定,同时我们没有使用 &mut 5 或者任何东西。所以谁给?

要理解这一点,我们需要回到 Rust 的指导哲学的核心,内存安全,和 Rust 保证的机制,所有权系统,和更具体的,借用:

You may have one or the other of these two kinds of borrows, but not both at the same time:

one or more references (&T) to a resource.
exactly one mutable reference (&mut T)

所以,这就是‘不变’的真正定义:有两个指针指向内容是否安全?在 Arc<T> 的例子中,是的:变化完全包含在内部结构本身中。它不是面向用户的。为此,将 &T 传递给 clone()。但是,如果将 &mut T 传递给 clone(),将会成为一个问题:

其它类型,诸如 std::cell 模块中,具有相反的:内部可变性。例如:

use std::cell::RefCell;

let x = RefCell::new(42);

let y = x.borrow_mut();

RefCell 用 borrow_mut() 函数来将 &mut 引用传递到它包含的东西中。那样不危险吗?如果那样做会怎样:

use std::cell::RefCell;

let x = RefCell::new(42);

let y = x.borrow_mut();
let z = x.borrow_mut();

事实上,这将在运行时引起恐慌。这是 RefCell 做的事情:它在运行时保证 Rust 的借用规则,同时如果它们违背了规则时的 panic!。这允许我们能够绕过 Rust 的不变规则的另一方面。让我们先讲讲吧。

字段级可变性

可变性是借用 ( &mut ) 或者绑定 ( let mut ) 的一个属性。这意味着,例如,你不能有一个结构体既有一些字段可变还有一些不可变:

struct Point {
x: i32,
mut y: i32, // nope
}

一个结构体在绑定方面的可变性:

展开阅读全文