# 可变性
> [mutability.md](https://github.com/rust-lang/rust/blob/master/src/doc/book/mutability.md)
commit 024aa9a345e92aa1926517c4d9b16bd83e74c10d
可变性,可以改变事物的能力,用在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) {
# }
~~~
### 内部可变性 VS 外部可变性(Interior vs. Exterior Mutability)
然而,当我们谈到Rust中什么是“不可变”的时候,它并不意味着它不能被改变:我们说它有“外部可变性”。例如,考虑下[`Arc<T>`](http://doc.rust-lang.org/nightly/std/sync/struct.Arc.html):
~~~
use std::sync::Arc;
let x = Arc::new(5);
let y = x.clone();
~~~
当我们调用`clone()`时,`Arc<T>`需要更新引用计数。以为你并未使用任何`mut`,`x`是一个不可变绑定,并且我们也没有取得`&mut 5`或者什么。那么发生了什么呢?
为了解释这些,我们不得不回到Rust指导哲学的核心,内存安全,和Rust用以保证它的机制,[所有权](#)系统,和更具体的[借用](#):
> 你可能有这两种类型借用的其中一个,但不同同时拥有:
> - 0个或N个对一个资源的引用(`&T`)
> - 正好1个可变引用(`&mut T`)
因此,这就是是“不可变性”的真正定义:当有两个引用指向同一事物是安全的吗?在`Arc<T>`的情况下,是安全的:改变完全包含在结构自身内部。它并不面向用户。为此,它用`clone()`分配`&T`。如果分配`&mut T`的话,那么,这将会是一个问题。
其它类型,像[std::cell](http://doc.rust-lang.org/nightly/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();
# (y, z);
~~~
事实上这会在运行时引起恐慌。这是`RefCell`如何工作的:它在运行时强制使用Rust的借用规则,并且如果有违反就会`panic!`。这让我们绕开了Rust可变性规则的另一方面。让我先讨论一下它。
### 字段级别可变性(Field-level mutability)
可变性是一个不是借用(`&mut`)就是绑定的属性(`&mut`)。这意味着,例如,你不能拥有一个一些字段可变而一些字段不可变的[结构体](#):
~~~
struct Point {
x: i32,
mut y: i32, // nope
}
~~~
结构体的可变性位于它的绑定上:
~~~
struct Point {
x: i32,
y: i32,
}
let mut a = Point { x: 5, y: 6 };
a.x = 10;
let b = Point { x: 5, y: 6};
b.x = 10; // error: cannot assign to immutable field `b.x`
~~~
然而,通过使用`Cell<T>`,你可以模拟字段级别的可变性:
~~~
use std::cell::Cell;
struct Point {
x: i32,
y: Cell<i32>,
}
let point = Point { x: 5, y: Cell::new(6) };
point.y.set(7);
println!("y: {:?}", point.y);
~~~
这会打印`y: Cell { value: 7 }`。我们成功的更新了`y`。
- 前言
- 贡献者
- 1.介绍
- 2.准备
- 3.学习 Rust
- 3.1.猜猜看
- 3.2.哲学家就餐问题
- 3.3.其它语言中的 Rust
- 4.语法和语义
- 4.1.变量绑定
- 4.2.函数
- 4.3.原生类型
- 4.4.注释
- 4.5.If语句
- 4.6.循环
- 4.7.所有权
- 4.8.引用和借用
- 4.9.生命周期
- 4.10.可变性
- 4.11.结构体
- 4.12.枚举
- 4.13.匹配
- 4.14.模式
- 4.15.方法语法
- 4.16.Vectors
- 4.17.字符串
- 4.18.泛型
- 4.19.Traits
- 4.20.Drop
- 4.21.if let
- 4.22.trait 对象
- 4.23.闭包
- 4.24.通用函数调用语法
- 4.25.crate 和模块
- 4.26.const和static
- 4.27.属性
- 4.28.type别名
- 4.29.类型转换
- 4.30.关联类型
- 4.31.不定长类型
- 4.32.运算符和重载
- 4.33.Deref强制多态
- 4.34.宏
- 4.35.裸指针
- 4.36.不安全代码
- 5.高效 Rust
- 5.1.栈和堆
- 5.2.测试
- 5.3.条件编译
- 5.4.文档
- 5.5.迭代器
- 5.6.并发
- 5.7.错误处理
- 5.8.选择你的保证
- 5.9.外部函数接口
- 5.10.Borrow 和 AsRef
- 5.11.发布途径
- 5.12.不使用标准库
- 6.Rust 开发版
- 6.1.编译器插件
- 6.2.内联汇编
- 6.4.固有功能
- 6.5.语言项
- 6.6.链接进阶
- 6.7.基准测试
- 6.8.装箱语法和模式
- 6.9.切片模式
- 6.10.关联常量
- 6.11.自定义内存分配器
- 7.词汇表
- 8.语法索引
- 9.参考文献
- 附录:名词中英文对照