variable-bindings.md
commit 6ba952020fbc91bad64be1ea0650bfba52e6aab4
事實(shí)上每一個(gè)非“Hello World” Rust 程序都用了變量綁定。他們將一些值綁定到一個(gè)名字上,這樣可以在之后使用他們。let
被用來聲明一個(gè)綁定,像這樣:
fn main() {
let x = 5;
}
在每個(gè)例子中都寫上fn main() {
有點(diǎn)冗長(zhǎng),所以之后我們將省略它。如果你是一路看過來的,確保你寫了main()
函數(shù),而不是省略不寫。否則,你將得到一個(gè)錯(cuò)誤。
在許多語言中,這叫做變量。不過 Rust 的變量綁定有一些不同的巧妙之處。例如let
表達(dá)式的左側(cè)是一個(gè)“[模式](Patterns 模式.md)”,而不僅僅是一個(gè)變量。這意味著我們可以這樣寫:
let (x, y) = (1, 2);
在這個(gè)表達(dá)式被計(jì)算后,x
將會(huì)是1,而y
將會(huì)是2.模式非常強(qiáng)大,并且本書中有[關(guān)于它的部分](Patterns 模式.md)。我們現(xiàn)在還不需要這些功能,所以接下來你只需記住有這個(gè)東西就行了。
Rust 是一個(gè)靜態(tài)類型語言,這意味著我們需要先確定我們需要的類型。那為什么我們第一個(gè)例子能編譯過呢?好的,Rust有一個(gè)叫做類型推斷的功能。如果它能確認(rèn)這是什么類型,Rust 不需要你明確地指出來。
若你愿意,我們也可以加上類型。類型寫在一個(gè)冒號(hào)(:
)后面:
let x: i32 = 5;
如果我叫你對(duì)著全班同學(xué)大聲讀出這一行,你應(yīng)該大喊“x
被綁定為i32
類型,它的值是5
”。
在這個(gè)例子中我們選擇x
代表一個(gè) 32 位的有符號(hào)整數(shù)。Rust 有許多不同的原生整數(shù)類型。以i
開頭的代表有符號(hào)整數(shù)而u
開頭的代表無符號(hào)整數(shù)??赡艿恼麛?shù)大小是 8,16,32 和 64 位。
在之后的例子中,我們可能會(huì)在注釋中注明變量類型。例子看起來像這樣:
fn main() {
let x = 5; // x: i32
}
注意注釋和let
表達(dá)式有類似的語法。理想的 Rust 代碼中不應(yīng)包含這類注釋。不過我們偶爾會(huì)這么做來幫助你理解 Rust 推斷的是什么類型。
綁定默認(rèn)是不可變的(immutable)。下面的代碼將不能編譯:
let x = 5;
x = 10;
它會(huì)給你如下錯(cuò)誤:
error: re-assignment of immutable variable `x`
x = 10;
^~~~~~~
如果你想一個(gè)綁定是可變的,使用mut
:
let mut x = 5; // mut x: i32
x = 10;
不止一個(gè)理由使得綁定默認(rèn)不可變的,不過我們可以通過一個(gè) Rust 的主要目標(biāo)來理解它:安全。如果你沒有使用mut
,編譯器會(huì)捕獲它,讓你知道你改變了一個(gè)你可能并不打算讓它改變的值。如果綁定默認(rèn)是可變的,編譯器將不可能告訴你這些。如果你確實(shí)想變量可變,解決辦法也非常簡(jiǎn)單:加個(gè)mut
。
盡量避免可變狀態(tài)有一些其它好處,不過這不在這個(gè)教程的討論范圍內(nèi)。大體上,你總是可以避免顯式可變量,并且這也是 Rust 希望你做的。即便如此,有時(shí),可變量是你需要的,所以這并不是被禁止的。
Rust 變量綁定有另一個(gè)不同于其它語言的方面:綁定要求在可以使用它之前必須初始化。
讓我們嘗試一下。將你的src/main.rs
修改為為如下:
fn main() {
let x: i32;
println!("Hello world!");
}
你可以用cargo build
命令去構(gòu)建它。它依然會(huì)輸出“Hello, world!”,不過你會(huì)得到一個(gè)警告:
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 警告我們從未使用過這個(gè)變量綁定,但是因?yàn)槲覀儚奈从眠^它,無害不罰。然而,如果你確實(shí)想使用x
,事情就不一樣了。讓我們?cè)囈幌?。修改代碼如下:
fn main() {
let x: i32;
println!("The value of x is: {}", x);
}
然后嘗試構(gòu)建它。你會(huì)得到一個(gè)錯(cuò)誤:
$ 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 是不會(huì)讓我們使用一個(gè)沒有經(jīng)過初始化的值的。接下來,讓我們討論一下我們添加到println!
中的內(nèi)容。
如果你輸出的字符串中包含一對(duì)大括號(hào)({}
,一些人稱之為胡須。。(譯注:moustaches,八字胡)),Rust將把它解釋為插入值的請(qǐng)求。字符串插值(String interpolation)是一個(gè)計(jì)算機(jī)科學(xué)術(shù)語,代表“在字符串中插入值”。我們加上一個(gè)逗號(hào),然后是一個(gè)x
,來表示我們想插入x
的值。逗號(hào)用來分隔我們傳遞給函數(shù)和宏的參數(shù),如果你想傳遞多個(gè)參數(shù)的話。
當(dāng)你只寫了大括號(hào)的時(shí)候,Rust 會(huì)嘗試檢查值的類型來顯示一個(gè)有意義的值。如果你想指定詳細(xì)的語法,有很多選項(xiàng)可供選擇?,F(xiàn)在,讓我們保持默認(rèn)格式,整數(shù)并不難打印。
讓我們回到綁定。變量綁定有一個(gè)作用域 - 他們被限制只能在他們被定義的塊中存在。一個(gè)塊是一個(gè)被{
和}
包圍的語句集合。函數(shù)定義也是塊!在下面的例子中我們定義了兩個(gè)變量綁定,x
和y
,他們位于不同的作用域中。x
可以在fn main() {}
塊中被訪問,而y
只能在內(nèi)部塊內(nèi)訪問:
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
}
第一個(gè)println!
將會(huì)打印“The value of x is 17 and the value of y is 3”,不過這個(gè)并不能編譯成功,因?yàn)榈诙€(gè)println!
并不能訪問y
的值,因?yàn)樗巡辉谧饔糜蛑小O喾次覀兊玫饺缦洛e(cuò)誤:
$ 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.
另外,變量可以被隱藏。這意味著一個(gè)后聲明的并位于同一作用域的相同名字的變量綁定將會(huì)覆蓋前一個(gè)變量綁定:
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"
隱藏和可變綁定可能作為同一枚硬幣的兩面出現(xiàn),不過他們是兩個(gè)并不總是能交替使用的不同的概念。作為其中之一,隱藏允許我們重綁定一個(gè)值為不同的類型。它也可以改變一個(gè)綁定的可變性:
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