Rust中的變量的聲明和定義


變量的聲明和定義

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 }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM