理解Rust中的變量賦值
Rust中使用let
聲明變量:
fn main(){ // 聲明變量name並初始化賦值 let name = "junmajinlong.com"; println!("{}", name); // println!()格式化輸出數據 }
Rust會對未使用的變量發出警告信息。如果確實想保留從未被使用過的變量,可在變量名前加上_
前綴。
fn main(){ let name = "junmajinlong.com"; println!("{}", name); let gender = "male"; // 警告,gender未使用 let _age = 18; // 加_前綴的變量不被警告 }
Rust允許聲明未被初始化(即未被賦值)的變量,但不允許使用未被賦值的變量。多數情況下,都是聲明的時候直接初始化的。
fn main() { let name; // 只聲明,未初始化 // println!("{}", name); // 取消該行註釋,將編譯錯誤 name = "junmajinlong.com"; println!("{}", name); }
Rust允許重複聲明同名變量,後聲明的變量將遮蓋(shadow)前面已聲明的變量。需注意的是,遮蓋不是覆蓋,被遮蓋的變量仍然存在,而如果是被覆蓋則不再存在(也即,覆蓋時,原數據會被銷燬)。
fn main() { let name = "junmajinlong.com"; // 註釋下行,將警告:name變量未被使用 // 因為name仍然存在,只是被遮蓋了 println!("{}", name); let name = "gaoxiaofang.com"; // 遮蓋已聲明的name變量 println!("{}", name); }
變量遮蓋示意圖:
注:下圖內存佈局並不完全正確,此圖僅為說明變量遮蓋
+---------+ +--------------------+
| Stack | | Heap |
+---------+ +--------------------+
name --> | 0x56789 | ---> | "gaoxiaofang.com" |
| | +--------------------+
name --> | 0x01234 | ---> | "junmajinlong.com" |
+---------+ +--------------------+
變量初始化後,默認不允許再修改該變量。注意,修改變量是直接給變量賦值,而不是再次let聲明該變量,再次聲明變量是允許的,它會遮蓋原變量。
fn main() { let name = "junmajinlong.com"; // 取消下行註釋將編譯錯誤,默認不允許修改變量 // name = "gaoxiaofang.com"; let name = "gaoxiaofang.com"; // 再次聲明變量,遮蓋變量 println!("{}", name); }
如果想要修改變量的值,需要在聲明變量時加上mut
標記(mutable)表示該變量是可修改的。
fn main() { let mut name = "junmajinlong.com"; println!("{}", name); name = "gaoxiaofang.com"; // 修改變量 println!("{}", name); }
Rust不僅對未被使用過的變量發出警告,還對賦值過但未被使用過的值發出警告。比如變量賦值後,尚未讀取該變量,就重新賦值了。
fn main() { let mut name = "junmajinlong.com"; // 警告值未被使用過 name = "gaoxiaofang.com"; println!("{}", name); }
Rust是靜態語言,聲明變量時需指定該變量將要保存的值的數據類型,這樣編譯器編譯時才知道為該變量將要保存的數據分配多少內存、允許存放什麼類型的數據以及如何存放數據。但Rust編譯器會根據所保存的值來推導變量的數據類型,推導得到確定的數據類型之後(比如第一次為該變量賦值之後),就不再允許存放其他類型的數據。
fn main() { // 根據保存的值推導數據類型 // 推導結果:變量name為 &str 數據類型 let mut name = "junmajinlong.com"; //name = 32; // 再讓name保存i32類型的數據,報錯 }
當Rust無法推導類型時,或者聲明變量時就明確知道該變量要保存聲明類型的數據時,可明確指定該變量的數據類型。
fn main() { // 指定變量數據類型的語法:在變量名後加": TYPE" let age: i32 = 32; // 明確指定age為i32類型 println!("{}", name); // i32類型的變量想存儲u8類型數據,不允許 // age = 23_u8; }
雖然Rust是基於表達式的語言,但變量聲明的let代碼是語句而非表達式。這意味著let操作沒有返回值,因此無法使用let來連續賦值。
fn main(){ let a = (let b = 1); // 錯誤 }
可以使用tuple的方式同時為多個變量賦值,並且可以使用下劃線_
佔位表示忽略某個變量的賦值過程。
#![allow(unused)] fn main() { // x = 11, y = 22, 忽略33 let (x, y, _) = (11, 22, 33); }
事實上,_
佔位符比想象中還更會【偷懶】,其他語言中_
表達的含義可能是丟棄其賦值結果(甚至不丟棄),但Rust中的_
會直接忽略變量賦值的過程。這導致了這樣一種看似奇怪的現象:使用普通變量名會導致報錯的變量賦值行為,使用_
卻不會報錯。
例如,下面(1)不會報錯,而(2)會報錯。這裡涉及到了後面所有權轉移的內容,如果看不懂請先跳過,只需記住結論:_
會直接忽略賦值的過程。
#![allow(unused)] fn main() { // (1) let s1 = "junmajinlong.com".to_string(); let _ = s1; println!("{}", s1); // 不會報錯 // (2) let s2 = "junmajinlong.com".to_string(); let ss = s2; println!("{}", s2); // 報錯 }
最後要說明的是,Rust中變量賦值操作實際上是Rust中的一種模式匹配,在後面的章節中將更系統、更詳細地介紹Rust模式匹配功能。