banner
davirain

davirain

twitter
github
知乎
twitter

變量的定義和聲明

Rust 中合法的標識符(包括變量名、函數名、trait 名等)必須由數字、字母、下劃線組成,而且不能以數字開頭。這個和很多語言都是一樣的。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 編譯器必須報錯。

let variable: i32;
println!("variable  = {}", variable); // error[E0381]: use of possibly unintialized 'variable'

檢測分支流程是否產生為初始化變量

Rust 編譯器的靜態分支流程分析比較嚴格。

fn main() {
    let x: i32;
    if true {
        x = 1;
    } else {
        x = 2;
    }
    println!("x = {}", x);
}

這裡的 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 表達式中已經綁定了值,所以編譯正常。

// 有一個例子
fn test(condition: bool ){
    let x: i32; //聲明x
    if condition {
        x = 1; //初始化x,這裡是初始化
        println!("{}", x); 
    }
    // 如果條件不滿足,x沒有被初始化

    //但是沒有關係,只要這裡不使用x就沒有事
}

檢測循環中是否產生未初始化變量

當循環中使用 break 關鍵字的時候,break 會將分支中的變量值返回。

fn main() {
    let x : i32;
    loop {
        if true {
            x = 2;
            break;
        }
    }
    println!("{}", x);// 2
}

Rust 編譯器的靜態分支流程分析知道,break 會將 x 的值返回,這樣 loop 循環之外 println! 可以正常打印 x 的值
空數組或向量可以初始化變量

當變量綁定空的數組或向量時,需要顯式制定類型,否則編譯器無法推斷其類型。

fn main() {
    let a: Vec<i32> = vec![];
    let b: [i32; 0] = [];
}

要是不加顯式類型標註的話,編譯器就會報錯: error[e0282]: type annotation needed 空數組或向量可以用來初始化變量,但目前暫時無法用於初始化常量或靜態變量。
轉移了所有權產生了未初始化變量

當將一個已經初始化的變量 y 綁定給另外一個變量 y2 時,Rust 會把 y 看做邏輯上的未初始化變量。 這裡的 y 和 y2 都是移動語義的變量,移動語義的變量會發生所有權的交接,而值語義,就像其他 C++ 語言默認的都是傳值。

fn main() {
    let x = 42; //原生類型都是值語義,默認存儲在棧上,
    let y = Box::new(4); //變量是由Box裝箱到堆上的, Box::new方法在堆上分配內存返回指針
    //並與y綁定,而指針y存儲在棧上,
    println!("{}", y);
    let x2 = x;
    let y2 = y;
    //println!("{}", y);//發生了所有權的轉移,所以這裡的變量y可以看做沒有未被初始化的變量
    //但是如果重新給變量綁定上一個值,變量y依然是可用的,這個過程叫做重新初始化
}
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。