變量的聲明和定義
Rust中合法的標識符(包括變量名、函數名、triat名等)必須由數字、字母、下划線組成,而且不能以數字開頭。這個和很多語言都是一樣的。Rust將來也會允許其他Unicode字符作為標識符,還有raw identifier功能,這樣可以使關鍵字作為標識符,比如r#self,這個用途在FFI中最多。
變量的聲明: let variable : i32 = 100; , 在rust中采用的變量的聲明方式不同於以往語言的聲明方式,這里先看變量的聲明, 變量的聲明時,是變量的名字先在前,而變量的類型在后面,let variable: i32;就是這個樣子。
這樣變量聲明的好處是,對於語法分析來說分析更為方便,並且在變量聲明語句中最為重要的是變量的名字,將變量名提前突出顯式變量名的重要性,對於類型是變量名的附加說明,可以通過上下文推導出變量的類型,當讓rust的自動類型推導具有局限性,對於不能推導出來的類型,需要手動添加類型說明。
變量聲明中的let的使用也是借鑒了函數式語言的思想,let表明的是綁定的含義,表示的是將變量名和內存作了一層綁定關系。在Rust中,一般把聲明的局部變量並初始化的語句稱為”變量綁定“, 這里強調的是”綁定“的含義,這里和C++/C中的”賦值初始化語句有所不同。
變量定義的一些問題
Rust中,每個變量必須被合理初始化之后才能被使用,使用未初始化變量這樣的錯誤,在rust中是不可能出現的。
檢查是否聲明初始化變量
剛剛上面的let variable : i32;這個是聲明,而沒有給變量賦值,這個在別的語言中可能是行的通的,但是在rust中,編譯器直接報錯(如果在后面使用這個為賦值(定義)的變量, Rust編譯器會對代碼作基本的靜態分支流程分析,確保變量在使用之前一定被初始化,variable沒有綁定任何值,這樣的代碼會引起很多內存不安全的問題,比如計算結果非預期、程序崩潰,所以Rust編譯器必須報錯。
1 let variable: i32;
2 println!("variable = {}", variable); // error[E0381]: use of possibly unintialized 'variable'
檢測分支流程是否產生為初始化變量
Rust編譯器的靜態分支流程分析比較嚴格。
1 fn main() { 2 let x: i32; 3 if true { 4 x = 1; 5 } else { 6 x = 2; 7 } 8 println!("x = {}", x); 9 }
這里的if分支的所有情況都給變量x綁定了值,所以它可以運行。但是如果去掉else分支,編譯器就會報錯:
error: use of possibly unintialized variable : 'x'
println!("x = {}", x);
從這里可以看到編譯器已經檢查出來變量x沒有被正確的初始化。去掉else分支后,編譯器的靜態分支流程分析判斷出在if表達式之外的println!也用到了變量x,但並未有綁定任何值得行為。編譯器的靜態分支流程分析並不能識別if表達式中的條件是true, 所以他要檢查所有分支的情況。(這個在程序設計語言領域中有專門去做研究的,比如軟件的靜態分析,一些參考材料:南京大學的軟件分析課程)
要是在把println!語句也去掉的話,則可以正常編譯運行,這是因為if表達式之外再也沒有任何地方使用變量x, 在唯一使用到x的if表達式中已經綁定了值,所以編譯正常。
1 // 有一個例子 2 fn test(condition: bool ){ 3 let x: i32; //聲明x 4 if condition { 5 x = 1; //初始化x,這里是初始化 6 println!("{}", x); 7 } 8 // 如果條件不滿足,x沒有被初始化 9 10 //但是沒有關系,只要這里不使用x就沒有事 11 }
檢測循環中是否產生未初始化變量
當循環中使用break關鍵字的時候,break會將分支中的變量值返回。
1 fn main() { 2 let x : i32; 3 loop { 4 if true { 5 x = 2; 6 break; 7 } 8 } 9 println!("{}", x);// 2 10 }
Rust編譯器的靜態分支流程分析知道,break會將x的值返回,這樣loop循環之外println!可以正常打印x的值
空數組或向量可以初始化變量
當變量綁定空的數組或向量時,需要顯式制定類型,否則編譯器無法推斷其類型。
1 fn main() { 2 let a: Vec<i32> = vec![]; 3 let b: [i32; 0] = []; 4 }
要是不加顯式類型標注的話,編譯器就會報錯: error[e0282]: type annotation needed 空數組或向量可以用來初始化變量,但目前暫時無法用於初始化常量或靜態變量。
轉移了所有權產生了未初始化變量
當將一個已經初始化的變量y綁定給另外一個變量y2時,Rust會把y看做邏輯上的未初始化變量。 這里的y和y2都是移動語義的變量,移動語義的變量會發生所有權的交接,而值語義,就像其他C++語言默認的都是傳值。
1 fn main() { 2 let x = 42; //原生類型都是值語義,默認存儲在棧上, 3 let y = Box::new(4); //變量是由Box裝箱到堆上的, Box::new方法在堆上分配內存返回指針 4 //並與y綁定,而指針y存儲在棧上, 5 println!("{}", y); 6 let x2 = x; 7 let y2 = y; 8 //println!("{}", y);//發生了所有權的轉移,所以這里的變量y可以看做沒有未被初始化的變量 9 //但是如果重新給變量綁定上一個值,變量y依然是可用的,這個過程叫做重新初始化 10 }
