天天看點

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

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

雖然有預設不變性還有所有權的問題讓Rust一上來用起來有些不同,但是其實大部分文法特點還是我們所熟悉的。

我們沒必要上來就跟自己死磕,可以先從我們熟悉的部分開始學習。

一般我們寫代碼,使用的主要是資料類型、控制結構和函數。我們就從這三部分開始。

資料類型

與Go一樣,Rust的定義語句資料也是放在變量名後面的,不過還要加上一個冒号。

布爾類型

布爾類型是bool:

let b0 : bool = true;           

因為Rust是有類型推斷的功能,是以很多時候可以不用指定類型。

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

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

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

字元類型 - 傳統與現代的結合

Rust的字元類型支援的是Unicode類型,占用4個位元組。同時,Rust也支援單位元組ASCII值,這時用b開頭,類型值就是8位無符号類型u8。

我們來看例子:

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

同樣,我們可以将字元組成字元串,我們來看例子:

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

輸出結果為:

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

s1的真實類型是str類型,而s2是u8的數組。

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

整數類型: 字尾與下劃線齊飛

按照長度,Rust的整數類型支援8位,16位,32位,64位,128位。根據有符号和無符号,分為有符号的i8,i16,i32,i64,i128和無符号的u8,u16,u32,u64,u128。

除此之外,也有根平台相關的類型,有符号為isize類型,無符号為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;           

上面都是跟其它語言比較像,下面我們來看看Rust特色的字尾。這在C++中也有,比如10l, 200L之類的。

在Rust中,我們直接用類型名做為字尾,我們看個例子:

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

這樣放在一起可能不太容易區分,沒關系,Rust允許我們在數字上任意的加下劃線來提升可讀性,我們來看幾個例子:

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

下劃線并非隻是用于數字和類型區分,也可以加在數字中間,我們來看個例子:

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

預設的整數類型是i32,如果Rust無法推斷中整數的類型,那麼就預設為i32.

整數的進制

在Rust中,避免了077這樣對八進制的偏愛,改為用0o來表示8進制整數。16進制仍然是0xFF字首,二進制用0b字首。

我們看例子:

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

整數的溢出

在C語言中,整數的溢出也是一個常出現的問題。

對此,Rust在debug模式下,在編譯時會檢查整數的溢出的問題:

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

在編譯時,Rust就會報錯:

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

懂程式分析的同學可能會想,在編譯時檢查不出來怎麼辦?好辦,我們在運作時進行檢查。

我們來個例子:

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

在運作時仍然發現了溢出:

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.           

類型轉換

Rust語言是強類型的語言,不像C一樣有預設的類型轉換。如果進行跨類型計算需要進行類型轉換。

類型轉換使用“as 類型”的方法來寫,我們來看個例子:

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

i8計算之後還是i8,不能直接賦給i32類型,需要通過as i32來轉換類型。

如果計算的類型不同,編譯不報錯,在運作的時候也會被檢查出來。

我們看個例子:

let i_101 = 16i8 + 32i32;           

會報下面的錯:

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

後面還有一個有趣的報錯,讓trait露了一個爪印:

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`           

浮點數

浮點數跟C語言差不多,分為32位浮點數和64位浮點數,就這兩種,分别是f32和f64。預設為f64。

我們來看兩個例子:

let f_01 = 2.1;
    let f_02 = 2e8;           

f_01和f_02都是f64類型。

需要注意的是,對于除0的處理,會引入兩個新的值:

  • 對于非0除以0,得到的将是無窮大inf
  • 而對于0除以0,将得到NaN,意思是并不是一個數
let f_03 = 0.0 / 0.0;
    let f_04 = 1.0 / 0.0;
    println!("{} {}",f_03,f_04);           
NaN inf           

NaN對應的本尊是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);           
NaN inf inf NaN           

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

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

但是要注意的是,兩個NAN是不相等的:

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

結果為false.

流程控制

分支語句

Rust支援if-else表達式,用來處理分支。

if後面不必加括号,有點像Go,我們看個例子:

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

可以寫成更像表達式一點的方式:

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

如果用作表達式的話,if和else兩個分支傳回的結果需要轉換成同一類型,畢竟Rust是這麼強類型的語言。

循環語句

Rust的循環分為三種:死循環loop,while循環和for循環。

loop最直接幹脆,不需要

while(true)

或者

for(;;)

這種寫法,直接loop。如果需要退出循環就用break,繼續下一輪循環就用continue。

我們來個簡單例子:

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

我們再将其翻譯成while循環:

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

與if一樣,while後面也不強制要求括号。

最後是for循環,它主要用于疊代器的周遊:

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

函數

最後說下函數,Rust的函數使用fn關鍵字來定義。傳回值的類型用->分隔而不是":"。

另外,Rust中不一定非要用return語句來傳回值,表達式的值即可,我們看個例子:

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

按傳統寫法也是可以的:

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

或者将return提到if表達式外面:

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

小結

在使用基本類型的情況下,Rust跟C語言和Go語言的基礎部分其實還是很類似的,熟悉Javascript等語言的同學也不會覺得陌生。我們可以把原有的知識遷移過來,基本類型變量如果需要修改值的話就加個mut。

繼續閱讀