# 可變性
> [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.參考文獻
- 附錄:名詞中英文對照