variable-bindings.md
commit 6ba952020fbc91bad64be1ea0650bfba52e6aab4
事實上每一個非“Hello World” Rust 程序都用了變量綁定。他們將一些值綁定到一個名字上,這樣可以在之后使用他們。let
被用來聲明一個綁定,像這樣:
fn main() {
let x = 5;
}
在每個例子中都寫上fn main() {
有點冗長,所以之后我們將省略它。如果你是一路看過來的,確保你寫了main()
函數(shù),而不是省略不寫。否則,你將得到一個錯誤。
在許多語言中,這叫做變量。不過 Rust 的變量綁定有一些不同的巧妙之處。例如let
表達式的左側(cè)是一個“[模式](Patterns 模式.md)”,而不僅僅是一個變量。這意味著我們可以這樣寫:
let (x, y) = (1, 2);
在這個表達式被計算后,x
將會是1,而y
將會是2.模式非常強大,并且本書中有[關(guān)于它的部分](Patterns 模式.md)。我們現(xiàn)在還不需要這些功能,所以接下來你只需記住有這個東西就行了。
Rust 是一個靜態(tài)類型語言,這意味著我們需要先確定我們需要的類型。那為什么我們第一個例子能編譯過呢?好的,Rust有一個叫做類型推斷的功能。如果它能確認這是什么類型,Rust 不需要你明確地指出來。
若你愿意,我們也可以加上類型。類型寫在一個冒號(:
)后面:
let x: i32 = 5;
如果我叫你對著全班同學大聲讀出這一行,你應(yīng)該大喊“x
被綁定為i32
類型,它的值是5
”。
在這個例子中我們選擇x
代表一個 32 位的有符號整數(shù)。Rust 有許多不同的原生整數(shù)類型。以i
開頭的代表有符號整數(shù)而u
開頭的代表無符號整數(shù)??赡艿恼麛?shù)大小是 8,16,32 和 64 位。
在之后的例子中,我們可能會在注釋中注明變量類型。例子看起來像這樣:
fn main() {
let x = 5; // x: i32
}
注意注釋和let
表達式有類似的語法。理想的 Rust 代碼中不應(yīng)包含這類注釋。不過我們偶爾會這么做來幫助你理解 Rust 推斷的是什么類型。
綁定默認是不可變的(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
。
盡量避免可變狀態(tài)有一些其它好處,不過這不在這個教程的討論范圍內(nèi)。大體上,你總是可以避免顯式可變量,并且這也是 Rust 希望你做的。即便如此,有時,可變量是你需要的,所以這并不是被禁止的。
Rust 變量綁定有另一個不同于其它語言的方面:綁定要求在可以使用它之前必須初始化。
讓我們嘗試一下。將你的src/main.rs
修改為為如下:
fn main() {
let x: i32;
println!("Hello world!");
}
你可以用cargo build
命令去構(gòu)建它。它依然會輸出“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);
}
然后嘗試構(gòu)建它。你會得到一個錯誤:
$ 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 是不會讓我們使用一個沒有經(jīng)過初始化的值的。接下來,讓我們討論一下我們添加到println!
中的內(nèi)容。
如果你輸出的字符串中包含一對大括號({}
,一些人稱之為胡須。。(譯注:moustaches,八字胡)),Rust將把它解釋為插入值的請求。字符串插值(String interpolation)是一個計算機科學術(shù)語,代表“在字符串中插入值”。我們加上一個逗號,然后是一個x
,來表示我們想插入x
的值。逗號用來分隔我們傳遞給函數(shù)和宏的參數(shù),如果你想傳遞多個參數(shù)的話。
當你只寫了大括號的時候,Rust 會嘗試檢查值的類型來顯示一個有意義的值。如果你想指定詳細的語法,有很多選項可供選擇?,F(xiàn)在,讓我們保持默認格式,整數(shù)并不難打印。
讓我們回到綁定。變量綁定有一個作用域 - 他們被限制只能在他們被定義的塊中存在。一個塊是一個被{
和}
包圍的語句集合。函數(shù)定義也是塊!在下面的例子中我們定義了兩個變量綁定,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
}
第一個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"
隱藏和可變綁定可能作為同一枚硬幣的兩面出現(xiàn),不過他們是兩個并不總是能交替使用的不同的概念。作為其中之一,隱藏允許我們重綁定一個值為不同的類型。它也可以改變一個綁定的可變性:
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