# 變量綁定
> [variable-bindings.md](https://github.com/rust-lang/rust/blob/master/src/doc/book/variable-bindings.md)
commit 6ba952020fbc91bad64be1ea0650bfba52e6aab4
事實上每一個非“Hello World” Rust 程序都用了*變量綁定*。他們將一些值綁定到一個名字上,這樣可以在之后使用他們。`let`被用來聲明一個綁定,像這樣:
~~~
fn main() {
let x = 5;
}
~~~
在每個例子中都寫上`fn main() {`有點冗長,所以之后我們將省略它。如果你是一路看過來的,確保你寫了`main()`函數,而不是省略不寫。否則,你將得到一個錯誤。
### 模式(Patterns)
在許多語言中,這叫做*變量*。不過 Rust 的變量綁定有一些不同的巧妙之處。例如`let`表達式的左側是一個“[模式](#)”,而不僅僅是一個變量。這意味著我們可以這樣寫:
~~~
let (x, y) = (1, 2);
~~~
在這個表達式被計算后,`x`將會是1,而`y`將會是2.模式非常強大,并且本書中有[關于它的部分](#)。我們現在還不需要這些功能,所以接下來你只需記住有這個東西就行了。
### 類型注解(Type annotations)
Rust 是一個靜態類型語言,這意味著我們需要先確定我們需要的類型。那為什么我們第一個例子能編譯過呢?好的,Rust有一個叫做*類型推斷*的功能。如果它能確認這是什么類型,Rust 不需要你明確地指出來。
若你愿意,我們也可以加上類型。類型寫在一個冒號(`:`)后面:
~~~
let x: i32 = 5;
~~~
如果我叫你對著全班同學大聲讀出這一行,你應該大喊“`x`被綁定為`i32`類型,它的值是`5`”。
在這個例子中我們選擇`x`代表一個 32 位的有符號整數。Rust 有許多不同的原生整數類型。以`i`開頭的代表有符號整數而`u`開頭的代表無符號整數。可能的整數大小是 8,16,32 和 64 位。
在之后的例子中,我們可能會在注釋中注明變量類型。例子看起來像這樣:
~~~
fn main() {
let x = 5; // x: i32
}
~~~
注意注釋和`let`表達式有類似的語法。理想的 Rust 代碼中不應包含這類注釋。不過我們偶爾會這么做來幫助你理解 Rust 推斷的是什么類型。
### 可變性(Mutability)
綁定默認是*不可變的*(*immutable*)。下面的代碼將不能編譯:
~~~
let x = 5;
x = 10;
~~~
它會給你如下錯誤:
~~~
error: re-assignment of immutable variable `x`
x = 10;
^~~~~~~
~~~
如果你想一個綁定是可變的,使用`mut`:
~~~
let mut x = 5; // mut x: i32
x = 10;
~~~
不止一個理由使得綁定默認不可變的,不過我們可以通過一個 Rust 的主要目標來理解它:安全。如果你沒有使用`mut`,編譯器會捕獲它,讓你知道你改變了一個你可能并不打算讓它改變的值。如果綁定默認是可變的,編譯器將不可能告訴你這些。如果你確實想變量可變,解決辦法也非常簡單:加個`mut`。
盡量避免可變狀態有一些其它好處,不過這不在這個教程的討論范圍內。大體上,你總是可以避免顯式可變量,并且這也是 Rust 希望你做的。即便如此,有時,可變量是你需要的,所以這并不是被禁止的。
### 初始化綁定(Initializing bindings)
Rust 變量綁定有另一個不同于其它語言的方面:綁定要求在可以使用它之前必須初始化。
讓我們嘗試一下。將你的`src/main.rs`修改為為如下:
~~~
fn main() {
let x: i32;
println!("Hello world!");
}
~~~
你可以用`cargo build`命令去構建它。它依然會輸出“Hello, world!”,不過你會得到一個警告:
~~~
Compiling hello_world v0.0.1 (file:///home/you/projects/hello_world)
src/main.rs:2:9: 2:10 warning: unused variable: `x`, #[warn(unused_variable)] on by default
src/main.rs:2 let x: i32;
^
~~~
Rust 警告我們從未使用過這個變量綁定,但是因為我們從未用過它,無害不罰。然而,如果你確實想使用`x`,事情就不一樣了。讓我們試一下。修改代碼如下:
~~~
fn main() {
let x: i32;
println!("The value of x is: {}", x);
}
~~~
然后嘗試構建它。你會得到一個錯誤:
~~~
$ cargo build
Compiling hello_world v0.0.1 (file:///home/you/projects/hello_world)
src/main.rs:4:39: 4:40 error: use of possibly uninitialized variable: `x`
src/main.rs:4 println!("The value of x is: {}", x);
^
note: in expansion of format_args!
<std macros>:2:23: 2:77 note: expansion site
<std macros>:1:1: 3:2 note: in expansion of println!
src/main.rs:4:5: 4:42 note: expansion site
error: aborting due to previous error
Could not compile `hello_world`.
~~~
Rust 是不會讓我們使用一個沒有經過初始化的值的。接下來,讓我們討論一下我們添加到`println!`中的內容。
如果你輸出的字符串中包含一對大括號(`{}`,一些人稱之為胡須。。(譯注:moustaches,八字胡)),Rust將把它解釋為插入值的請求。*字符串插值*(*String interpolation*)是一個計算機科學術語,代表“在字符串中插入值”。我們加上一個逗號,然后是一個`x`,來表示我們想插入`x`的值。逗號用來分隔我們傳遞給函數和宏的參數,如果你想傳遞多個參數的話。
當你只寫了大括號的時候,Rust 會嘗試檢查值的類型來顯示一個有意義的值。如果你想指定詳細的語法,有[很多選項可供選擇](http://doc.rust-lang.org/std/fmt/)。現在,讓我們保持默認格式,整數并不難打印。
### 作用域和隱藏(Scope and shadowing)
讓我們回到綁定。變量綁定有一個作用域 - 他們被限制只能在他們被定義的塊中存在。一個塊是一個被`{`和`}`包圍的語句集合。函數定義也是塊!在下面的例子中我們定義了兩個變量綁定,`x`和`y`,他們位于不同的作用域中。`x`可以在`fn main() {}`塊中被訪問,而`y`只能在內部塊內訪問:
~~~
fn main() {
let x: i32 = 17;
{
let y: i32 = 3;
println!("The value of x is {} and value of y is {}", x, y);
}
println!("The value of x is {} and value of y is {}", x, y); // This won't work
}
~~~
第一個`println!`將會打印“The value of x is 17 and the value of y is 3”,不過這個并不能編譯成功,因為第二個`println!`并不能訪問`y`的值,因為它已不在作用域中。相反我們得到如下錯誤:
~~~
$ cargo build
Compiling hello v0.1.0 (file:///home/you/projects/hello_world)
main.rs:7:62: 7:63 error: unresolved name `y`. Did you mean `x`? [E0425]
main.rs:7 println!("The value of x is {} and value of y is {}", x, y); // This won't work
^
note: in expansion of format_args!
<std macros>:2:25: 2:56 note: expansion site
<std macros>:1:1: 2:62 note: in expansion of print!
<std macros>:3:1: 3:54 note: expansion site
<std macros>:1:1: 3:58 note: in expansion of println!
main.rs:7:5: 7:65 note: expansion site
main.rs:7:62: 7:63 help: run `rustc --explain E0425` to see a detailed explanation
error: aborting due to previous error
Could not compile `hello`.
To learn more, run the command again with --verbose.
~~~
另外,變量可以被隱藏。這意味著一個后聲明的并位于同一作用域的相同名字的變量綁定將會覆蓋前一個變量綁定:
~~~
let x: i32 = 8;
{
println!("{}", x); // Prints "8"
let x = 12;
println!("{}", x); // Prints "12"
}
println!("{}", x); // Prints "8"
let x = 42;
println!("{}", x); // Prints "42"
~~~
隱藏和可變綁定可能作為同一枚硬幣的兩面出現,不過他們是兩個并不總是能交替使用的不同的概念。作為其中之一,隱藏允許我們重綁定一個值為不同的類型。它也可以改變一個綁定的可變性:
~~~
let mut x: i32 = 1;
x = 7;
let x = x; // x is now immutable and is bound to 7
let y = 4;
let y = "I can also be bound to text!"; // y is now of a different type
~~~
- 前言
- 貢獻者
- 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.參考文獻
- 附錄:名詞中英文對照