107.05.05 rust 之路 05 型別

注意 Rust 為靜態語言 (static typing) 所以在編譯時期變數型別必須已知
但是又不需要囉嗦的每個都寫說是什麼型別,因為 Rust 有 Type inference 會自動推論

Rust 的資料型別有兩大類:純量 (Scalar)、複合型 (Compound)
純量有整數 (Integer)、浮點數 (Floating-Point)、Boolean、字元 (Character)
複合型有 tuple、陣列 (Array)
萬物形色多樣,卻又同由固定的基本元素構成,是型態構成型態?
Photo by Jeremy Thomas on Unsplash

整數

又細分成不同長度 (bit size)
一般來說系統程式語言都會分,因為較接近硬體所以應該更詳細定義使用的大小
當然能用更小的表示當然就用小的,較省空間
其中比較特別的是"系統架構"那列,它會依照執行程式的電腦的系統架構 (32 or 64) 去改變

長度 Signed Unsigned
8-bit i8 u8
16-bit i16 u16
32-bit i32 u32
64-bit i64 u64
系統架構 isize usize

其他特性:
後綴型別修飾 e.g. 57u8
視覺化的分隔 e.g. 1_000
一般來說,沒有寫就是預設使用 i32 (官方說 i32 執行最快)

上 code:(就不寫 fn main(){} 喔,這樣比較乾淨)
e.g. 1.
Rust 允許先宣告之後再定義給值 (宣告 vs 定義)
這裡就不需要 mut 喔,如果寫了編譯器也會發警告建議不要加 mut
let x; // 沒有宣告 x 的型別
x = 5; // 5 沒加後綴修飾就預設為 i32,所以編譯時就能確定 x 是 i32

e.g. 2.
宣告時加上型別,變數後方用":"加上型別
不加 into() 會 error,因為應該使用 u16 (和 x 相同)
所以 into() 是 Rust 針對小型別轉大型別而提供的函數
作法:unsigned 是直接補 0(zero-extend),signed 使用 sign-extend
使用 as 轉型可能會 overflow 但不會有 overflowing_literals 的警告
例如 256u32 as u8 就不是 256 而是 0,因為 u8 只有用 8-bit 存數字
let mut x: u16;  // 宣告 x 的型別是 u16
x = 5u8.into();  // 小轉大型別:使用 into() 轉型
x = 5u32 as u16; // 大轉小型別:使用 as 來轉型

e.g. 3.
let x: u16 = 5; // 宣告 x 的型別是 u16 並定義 x 的數值

e.g. 4.
比較一下想想為什麼吧 OuO
fn main() {
    let x = 256;
    let y = x + 8;
    println!("x: {}, y: {}",x, y) // x: 256, y: 264
}
fn main() {
    let x = 256;
    let y = x + 8u8;
    println!("x: {}, y: {}",x, y) // x: 0, y: 8
}

結果光是整數就這麼長篇幅 QuQ,再來是....

浮點數

根據IEEE-754標準
關鍵字:f32 (單精度)、f64 (倍精度)
預設為 f64,因為速度差不多
基本上用法與整數相同
let x = 2.0;      // f64
let y: f32 = 3.0; // f32

Boolean

關鍵字:bool
值有兩種:true、false
主要使用在流程控制 (condition、loop) 中
let t = true;
let f: bool = false; // 有型別標示

字元

關鍵字:char
不是用 ASCII 而是 Unicode,所以就需要 4 bytes 來存
說到 unicode 就代表所有符號都可以存,甚至包括中文、emoji
let c: char = 'z';
let z = 'ℤ';
let heart_eyed_cat = '😻';

tuple

這在 python 也有,也很像 Lisp 中的 values + multiple-value-bind
就是可以把不同型別用 () 包成一個型別
最方便的就是可以讓函式有多個回傳值 (事實上還是只有一個)
fn main() {
    let (i, c) = get_int_and_char();
    println!("{} {}", i, c);        // 5 c
    
    let x = get_int_and_char();
    println!("{} {}", x.0, x.1);    // 5 c
}

fn get_int_and_char() -> (i32, char) {
    (5i32, 'c') // 不加 ";" 會變成表達式(Expression),會回傳本身,之後會再提
}

陣列

這超常見的啊,應該不需要多做解釋
與 tuple 不同的就是內容必須要同型別
let a: [i32; 3] = [1, 2, 3]; // ": [i32; 3]" 可省略
println!("{}", a[2]);        // 3

複合型別超出範圍的存取

let x = (1, 'd');
println!("{}", x.2);  // --> 編譯時期:error[E0612]
let a = [1, 2];
println!("{}", a[2]); // --> 執行時期:panicked (編譯會過)
<107.06.02更新>
那為什麼呢?
原因是因為 tuple 存取只會出現數字 (.0, .1, .2),但是 array 可以出現變數 ([x], [y], [z])
而變數的值較難在編譯時期就決定,因此就會在執行時期中斷,同樣的 vector 也是如此
-
Make ‘index out of bounds’ a compile-time error


感覺還是不夠快啊 QuQ
謀斗骸雅庫~
下一篇 函式 (Function)

沒有留言:

張貼留言

^ Top