Rust語(yǔ)言教程(2) - 從熟悉的部分開始

Rust語(yǔ)言教程(2) - 從熟悉的部分開始

雖然有默認(rèn)不變性還有所有權(quán)的問(wèn)題讓Rust一上來(lái)用起來(lái)有些不同,但是其實(shí)大部分語(yǔ)法特點(diǎn)還是我們所熟悉的。
我們沒(méi)必要上來(lái)就跟自己死磕,可以先從我們熟悉的部分開始學(xué)習(xí)。

一般我們寫代碼,使用的主要是數(shù)據(jù)類型、控制結(jié)構(gòu)和函數(shù)。我們就從這三部分開始。

數(shù)據(jù)類型

與Go一樣,Rust的定義語(yǔ)句數(shù)據(jù)也是放在變量名后面的,不過(guò)還要加上一個(gè)冒號(hào)。

布爾類型

布爾類型是bool:

let b0 : bool = true;

因?yàn)镽ust是有類型推斷的功能,所以很多時(shí)候可以不用指定類型。

    let b1 = true;
    let b2 = !b1;
    let b3 = 1 > 0;
    println!("{} {} {}",b1,b2,b3);

如果使用CLion等IDE的話,就可以直接看到IDE提供的灰色的類型推斷的提示,非常方便:


CLion.png

字符類型 - 傳統(tǒng)與現(xiàn)代的結(jié)合

Rust的字符類型支持的是Unicode類型,占用4個(gè)字節(jié)。同時(shí),Rust也支持單字節(jié)ASCII值,這時(shí)用b開頭,類型值就是8位無(wú)符號(hào)類型u8。

我們來(lái)看例子:

    let c1 :char = 'C';
    let c2:u8 = b'C';
    let c3 = '中';
    println!("{} {} {}",c1,c2,c3);

同樣,我們可以將字符組成字符串,我們來(lái)看例子:

    let s1 = "Hello";
    let s2 = b"World";
    println!("{} {:?}",s1,s2);

輸出結(jié)果為:

Hello [87, 111, 114, 108, 100]

s1的真實(shí)類型是str類型,而s2是u8的數(shù)組。

    let s1 :&str = "Hello";
    let s2 :&[u8;5] = b"World";

整數(shù)類型: 后綴與下劃線齊飛

按照長(zhǎng)度,Rust的整數(shù)類型支持8位,16位,32位,64位,128位。根據(jù)有符號(hào)和無(wú)符號(hào),分為有符號(hào)的i8,i16,i32,i64,i128和無(wú)符號(hào)的u8,u16,u32,u64,u128。
除此之外,也有根平臺(tái)相關(guān)的類型,有符號(hào)為isize類型,無(wú)符號(hào)為usize類型。

我們看下例子:

    let i1 : i8 = -8;
    let i2 : i16 = -16;
    let i3 : i32 = -32;
    let i4 : i64 = -64;
    let i5 : i128 = -128;

    let u1 : u8 = 8;
    let u2 : u16 = 16;
    let u3 : u32 = 32;
    let u4 : u64 = 64;
    let u5 : u128 = 128;
    
    let p1 : isize = -1;
    let p2 : usize = 1;

上面都是跟其它語(yǔ)言比較像,下面我們來(lái)看看Rust特色的后綴。這在C++中也有,比如10l, 200L之類的。
在Rust中,我們直接用類型名做為后綴,我們看個(gè)例子:

    let i6 = -1i8;
    let i7 = -2i16;

這樣放在一起可能不太容易區(qū)分,沒(méi)關(guān)系,Rust允許我們?cè)跀?shù)字上任意的加下劃線來(lái)提升可讀性,我們來(lái)看幾個(gè)例子:

    let i08 = -3_i32;
    let i09 = -4__i64;
    let i10 = -5___i128;

下劃線并非只是用于數(shù)字和類型區(qū)分,也可以加在數(shù)字中間,我們來(lái)看個(gè)例子:

    let u6 = 1_000_000_u128;
    println!("{}",u6);

默認(rèn)的整數(shù)類型是i32,如果Rust無(wú)法推斷中整數(shù)的類型,那么就默認(rèn)為i32.

整數(shù)的進(jìn)制

在Rust中,避免了077這樣對(duì)八進(jìn)制的偏愛(ài),改為用0o來(lái)表示8進(jìn)制整數(shù)。16進(jìn)制仍然是0xFF前綴,二進(jìn)制用0b前綴。

我們看例子:

    let u07 = 0xFF_u32;
    let u08 = 0o7777_u32;
    let u09 = 0b01_10_00_u8;
    println!("{} {} {}",u07,u08,u09);

輸出結(jié)果為:

255 4095 24

整數(shù)的溢出

在C語(yǔ)言中,整數(shù)的溢出也是一個(gè)常出現(xiàn)的問(wèn)題。
對(duì)此,Rust在debug模式下,在編譯時(shí)會(huì)檢查整數(shù)的溢出的問(wèn)題:

    let i_10 : i8 = 0x7f;
    let i_11 : i8 = i_10 * 10i8;
    println!("{}",i_11);

在編譯時(shí),Rust就會(huì)報(bào)錯(cuò):

84 |     let i_11 : i8 = i_10 * 10i8;
   |                     ^^^^^^^^^^^ attempt to compute `i8::MAX * 10_i8`, which would overflow

懂程序分析的同學(xué)可能會(huì)想,在編譯時(shí)檢查不出來(lái)怎么辦?好辦,我們?cè)谶\(yùn)行時(shí)進(jìn)行檢查。
我們來(lái)個(gè)例子:

    let mut i_20 : i8 = 0x20;
    for i in 1..20{
        i_20 = 0x20_i8 * i_20;
    }
    println!("{}",i_20);

在運(yùn)行時(shí)仍然發(fā)現(xiàn)了溢出:

thread 'main' panicked at 'attempt to multiply with overflow', src/main.rs:91:16
stack backtrace:
   0: rust_begin_unwind
             at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/std/src/panicking.rs:483
   1: core::panicking::panic_fmt
             at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/panicking.rs:85
   2: core::panicking::panic
             at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/panicking.rs:50
   3: tools::test
             at ./src/main.rs:91
   4: tools::main
             at ./src/main.rs:34
   5: core::ops::function::FnOnce::call_once
             at /Users/lusinga/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:227
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

類型轉(zhuǎn)換

Rust語(yǔ)言是強(qiáng)類型的語(yǔ)言,不像C一樣有默認(rèn)的類型轉(zhuǎn)換。如果進(jìn)行跨類型計(jì)算需要進(jìn)行類型轉(zhuǎn)換。
類型轉(zhuǎn)換使用“as 類型”的方法來(lái)寫,我們來(lái)看個(gè)例子:

let i_100 : i32 = (16i8 + 1) as i32;

i8計(jì)算之后還是i8,不能直接賦給i32類型,需要通過(guò)as i32來(lái)轉(zhuǎn)換類型。

如果計(jì)算的類型不同,編譯不報(bào)錯(cuò),在運(yùn)行的時(shí)候也會(huì)被檢查出來(lái)。
我們看個(gè)例子:

let i_101 = 16i8 + 32i32;

會(huì)報(bào)下面的錯(cuò):

error[E0308]: mismatched types
  --> src/main.rs:99:24
   |
99 |     let i_101 = 16i8 + 32i32;
   |                        ^^^^^ expected `i8`, found `i32`

后面還有一個(gè)有趣的報(bào)錯(cuò),讓trait露了一個(gè)爪印:

error[E0277]: cannot add `i32` to `i8`
  --> src/main.rs:99:22
   |
99 |     let i_101 = 16i8 + 32i32;
   |                      ^ no implementation for `i8 + i32`
   |
   = help: the trait `Add<i32>` is not implemented for `i8`

浮點(diǎn)數(shù)

浮點(diǎn)數(shù)跟C語(yǔ)言差不多,分為32位浮點(diǎn)數(shù)和64位浮點(diǎn)數(shù),就這兩種,分別是f32和f64。默認(rèn)為f64。
我們來(lái)看兩個(gè)例子:

    let f_01 = 2.1;
    let f_02 = 2e8;

f_01和f_02都是f64類型。

需要注意的是,對(duì)于除0的處理,會(huì)引入兩個(gè)新的值:

  • 對(duì)于非0除以0,得到的將是無(wú)窮大inf
  • 而對(duì)于0除以0,將得到NaN,意思是并不是一個(gè)數(shù)

我們來(lái)看例子:

    let f_03 = 0.0 / 0.0;
    let f_04 = 1.0 / 0.0;
    println!("{} {}",f_03,f_04);

輸出結(jié)果為:

NaN inf

NaN對(duì)應(yīng)的本尊是std::f64::NAN,而inf是std::f64::INFINITY,我們將其排列在一起:

    let f_03 = 0.0 / 0.0;
    let f_04 = 1.0 / 0.0;
    let f_05 = std::f64::INFINITY;
    let f_06 = std::f64::NAN;
    println!("{} {} {} {}",f_03,f_04,f_05,f_06);

輸出結(jié)果為:

NaN inf inf NaN

32位和64位的無(wú)窮大都是無(wú)窮大,它們是相等的:

    let f_10 = std::f32::INFINITY;
    let f_11 = std::f64::INFINITY;
    println!("{}",f_11==f_10 as f64);

輸出結(jié)果為:

true

但是要注意的是,兩個(gè)NAN是不相等的:

    let f_12 = std::f64::NAN;
    println!("{}",f_12==f_12);

結(jié)果為false.

流程控制

分支語(yǔ)句

Rust支持if-else表達(dá)式,用來(lái)處理分支。
if后面不必加括號(hào),有點(diǎn)像Go,我們看個(gè)例子:

    if n >= 100 {
        println!("Grade A");
    }else if n>= 60 {
        println!("Pass");
    }else{
        println!("Fail");
    }

可以寫成更像表達(dá)式一點(diǎn)的方式:

    let grade = if n == 100{
        "A"
    }else if n>=60{
        "Pass"
    }else{
        "Fail"
    };

如果用作表達(dá)式的話,if和else兩個(gè)分支返回的結(jié)果需要轉(zhuǎn)換成同一類型,畢竟Rust是這么強(qiáng)類型的語(yǔ)言。

循環(huán)語(yǔ)句

Rust的循環(huán)分為三種:死循環(huán)loop,while循環(huán)和for循環(huán)。

loop最直接干脆,不需要while(true)或者for(;;)這種寫法,直接loop。如果需要退出循環(huán)就用break,繼續(xù)下一輪循環(huán)就用continue。

我們來(lái)個(gè)簡(jiǎn)單例子:


    let mut num = 0;
    let mut sum = 0;
    loop{
        if num > 10 {
            break;
        }else{
            sum += num;
            num += 1;
        }
    }
    println!("sum={}",sum);

我們?cè)賹⑵浞g成while循環(huán):

    num = 0;
    sum = 0;
    while num <= 10 {
        sum += num;
        num += 1;
    }
    println!("sum={}", sum);

與if一樣,while后面也不強(qiáng)制要求括號(hào)。

最后是for循環(huán),它主要用于迭代器的遍歷:

    sum = 0;
    for i in 0..11  {
        sum += i;
    }
    println!("sum={}", sum);

函數(shù)

最后說(shuō)下函數(shù),Rust的函數(shù)使用fn關(guān)鍵字來(lái)定義。返回值的類型用->分隔而不是":"。
另外,Rust中不一定非要用return語(yǔ)句來(lái)返回值,表達(dá)式的值即可,我們看個(gè)例子:

fn fib2(n: i32) -> i64 {
    if n <= 2 {
        1i64
    } else {
        fib2(n - 1) + fib2(n - 2)
    }
}

按傳統(tǒng)寫法也是可以的:

fn fib2(n: i32) -> i64 {
    if n <= 2 {
        return 1i64
    } else {
        return fib2(n - 1) + fib2(n - 2)
    }
}

或者將return提到if表達(dá)式外面:

fn fib2(n: i32) -> i64 {
    return if n <= 2 {
        1i64
    } else {
        fib2(n - 1) + fib2(n - 2)
    }
}

小結(jié)

在使用基本類型的情況下,Rust跟C語(yǔ)言和Go語(yǔ)言的基礎(chǔ)部分其實(shí)還是很類似的,熟悉Javascript等語(yǔ)言的同學(xué)也不會(huì)覺(jué)得陌生。我們可以把原有的知識(shí)遷移過(guò)來(lái),基本類型變量如果需要修改值的話就加個(gè)mut。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容