Rust 從入門到精通06-語句和表示式

2022-09-01 12:04:08

1、語句和表示式

語句和表示式是 Rust 語言實現邏輯控制的基本單元。 在 Rust 程式裡面,語句(Statement)是執行一些操作但不返回的指令,表示式(Expressions)計算併產生一個值。表示式可以是語句的一部分,反過來,語句也可以是表示式的一部分。

1.1 語句不返回值

fn main() {
    let x = (let y = 6);
}

這裡面let y = 6 是一個語句,不能把 let 語句賦值給另一個變數,否則編譯器會報錯。

1.2 表示式返回值

fn main() {
    let y = {
        let x = 3;
        x + 1
    };
    println!("The value of y is: {}", y);
}

{},也是一個表示式,表示式的結果是最後一行程式碼,x + 1後面沒有分號,表示的是表示式,如果在表示式後面加上「;」,則表示語句,語句沒有返回值,則上面程式碼會報錯。

1.3 總結

①、一個表示式總會產生一個值,因此它必然有型別。
②、語句不產生值,它的型別永遠是 ();
③、如果把一個表示式加上分號,那麼它就變成了一個語句;
④、如果把一個語句放到一個語句塊中包起來,那麼它就可以當成一個表示式使用。

Rust is primarily an expression language

翻譯過來:Rust 基本上就是一個表示式語言。

Rust 除了 let / static / const / fn 等少數語句外,Rust 絕大多數程式碼都是表示式(expression)。所以 if / while / for / loop 都會返回一個值,函數最後一個表示式就是函數的返回值,這和函數語言程式設計語言一致。

語句就是計算結果為()的特殊表示式。Rust 編譯器,在解析程式碼的時候,如果碰到分號,就會繼續往後執行。如果遇到語句,就執行語句;如果遇到表示式,則會對錶示式求值;如果分號後面什麼都沒有,就補上()。

2、算術表示式

2.1、算術運運算元:+ - * / %

分別是加、減、乘、除、取餘。

//加、減、乘、除、取餘
fn arithmetic_operation_test1(){
    let x = 100;
    let y = 10;
    println!("x={},y={},x+y={},x-y={},x*y={},x/y={},x%y={}",x,y,x+y,x-y,x*y,x/y,x%y);
}

2.2、比較運運算元

注意:
①、比較運運算元兩邊必須是同型別的,並且滿足 PartialEq 約束;
②、比較表示式的型別是 bool;
③、Rust 禁止連續比較;

fn compare_test(a:bool,b:bool,c:bool) -> bool{
    a==b==c
}

編譯報錯:

2.3、賦值表示式

一個左值表示式、賦值運運算元(=)、一個右值表示式可以構成一個賦值表示式。
①、賦值號左右兩邊表示式的型別必須一致,否則編譯報錯。
②、賦值表示式也有對應的型別和值,型別為 unit。即空的 tuple();

//賦值表示式也有對應的型別和值,型別為 unit
fn arithmetic_operation_test2(){
    let x = 1;
    let mut y = 2;

    let z = (y=x);
    //列印結果為()
    println!("{:?}",z);
}

這樣能防止連續賦值,假設定義了三個 i32 型別的變數, x:i32,y:i32以及z:i32, 那麼表示式 x=y=z就會發生編譯錯誤,因為z變數是i32型別,卻賦值(),編譯器是不允許的。

2.4、語句塊表示式

在Rust 中,語句塊也可以是表示式的一部分。

語句和表示式的區分方式是後面帶不帶分號,如果帶了分號,意味著這是一條語句,它的型別是();

如果沒有帶分號,它的型別就是表示式的型別。

//語句和表示式的區分方式是後面帶不帶分號,如果帶了分號,意味著這是一條語句,它的型別是();
//如果沒有帶分號,它的型別就是表示式的型別。
fn arithmetic_operation_test3(){
    //語句帶分號,型別是 ()
    let x:() = {println!("helloworld");};
    //Rust 將按照順序執行語句塊內的語句,並將最後的一個表示式型別返回,所以 y 最終型別是 i32
    let y = {println!("helloworld"); 5};
    println!("x={:?}",x);
    println!("y={}",y);
}

列印結果為:

2.5、if-else

①、條件表示式的型別必須是bool
②、條件表示式並未強制要求用小括號()括起來,如果括起來,編譯器反而會告警,認為是多餘的括號;
③、後面的結果語句塊一定要用大括號括起來;

//if-else
fn if_else_test()->i32{
    if (1>2) {
        //沒有加分號,返回值就是1
        1
    }else{
        2
    }
}

使用 if-else 作為表示式,一定要注意 if 和 else 分支的型別必須一致,否則就不能構成一個合法的表示式,會出現編譯錯誤。

最常見的一種情況是 if 分支有資料返回,但是省略了 else 分支:

fn if_test() -> i32{
    if true {
        1
    }
    return 1;
}

編譯報錯:

這是因為 else 分支如果省略了,預設型別是 ’()‘ ,與 if 分支不匹配。

2.6、loop

在Rust中,loop表示無限死迴圈。

//loop
fn loop_test(){
    let mut i = 0;
    loop{
        i += 1;
        if(i == 3){
            println!("three");
            //不在繼續執行後面的程式碼,直接跳轉到loop開頭繼續迴圈
            continue;
        }
        println!("{}",i);
        if(i == 5){
            println!("that's is OK");
            //跳出迴圈
            break;
        }
    }
}

continue 表示本次迴圈內,不在執行後面的語句,直接進入下一輪迴圈;
break 表示跳出迴圈,不在執行。

注意:在Rust中,我們可以在 loop、while、for迴圈前面加上「生命週期標識」,在內部迴圈中,可以通過break、continue選擇跳轉到哪個迴圈標識。

2.7、while

帶條件判斷的迴圈語句。

//while迴圈
fn while_test(){
    let mut n = 1;
    while(n < 100){
        if(n%2==0){
            println!("偶數:{}",n)
        }else{
            println!("奇數:{}",n)
        }
        n+=1;
    }
}

2.8、loop{} 和 while(true){}

從語法上理解,loop{} 和 while(true){} 這兩種是沒有任何區別的。
但相比於其他很多語言,Rust 語言要做更多的靜態分析,loop 和 while true 語句在執行時沒有任何區別,他們主要會影響編譯器內部的靜態分析結果。
比如:

let x;
loop{
    x = 1;
    break;
}
println!("{}",x);

上面語句在Rust中完全合理,因為編譯器可以通過流程分析推理出x=1,必然在println!之前執行過,所以列印x的值是完全合理的。
再比如對於while true 語句:

let x;
while(true){
    x = 1;
    break;
}
println!("{}",x);

報錯如下:

因為編譯器會覺得while 語句的執行和條件表示式在執行階段的值有關(有可能while false,導致沒有執行 while 裡面的語句,從而 x 沒有初始化),於是編譯器直接丟擲一個未初始化異常。

2.9、for

Rust 中的for迴圈類似其他語言中的 for-each 迴圈。
for迴圈的主要用處是利用迭代器對包含同樣型別的多個元素的容器進行遍歷,如陣列、連結串列、HashMap、HashSet等。

fn for_test(){
    let array = &[1,2,3,4,5];
    for i in array {
        println!("The Numer is {}",i);
    }
}

3、常見錯誤

3.1 連續賦值報錯

fn f(a:bool,b:bool,c:bool) -> bool{
    a == b == c
}

報錯如下:

3.2 漏掉 else 分支報錯

如果 else 分支省略掉了,編譯器會認為 else 分支的型別預設為(),但是 if 分支返回的是 i32 資料型別。

我們知道,使用 if-else 作為表示式,一定要注意 if 和 else 分支的型別必須一致,否則就不能構成一個合法的表示式,會出現編譯錯誤。

fn if_test() -> i32{
    if true {
        0
    }
    return 1;
}

編譯報錯: