iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 8
1

各位好又見面了,今天要和各位介紹 Rust 的資料型態而想必有寫過程式的應該對基本的資料型態都不陌生,那麼就讓我們開始介紹吧!

強型別

Rust 是屬於強型別的靜態語言,也就是說不會有隱藏的自動型別轉換,舉例來說在 javascript 裡面我們可以這樣寫。

const foo = 'foo'
const bar = 3
console.log(foo + bar)
// foo3

而 Rust 是不能這樣做的

fn wrong_types() {
  let x = 30.5;
  let y = 30;
  println!("The value of x + y is: {}", x + y);
  // ^ no implementation for `{float} + {integer}`
}

Note:由於 x 的類型是浮點數(f64)所以不能和整數 y 做運算。

雖然 Rust 屬於強型別的語言但是也無需特別宣告其類型,他會在編譯時會自動針對你的值給予型態。

fn data_types() {
  let company_string = "TutorialsPoint";  // string type
  let age = 25;                           // integer type
  let rating_float = 4.5;                 // float type
  let is_growing_boolean = true;          // boolean type
  let icon_char = '♥';                    // unicode character type
}

整數

讓我們先來看一下官方的這張表,

https://ithelp.ithome.com.tw/upload/images/20190923/20119807TBGJ1hynCh.png

數字代表位元數也就是我們常說的 32 位元或是 64 位元的電腦系統代表可以處理的數字是 2 的 n 幾次方。另外u(standing for unsigned 沒有符號的整數) 或是 i(standing for integer 正或負整數) 我也是第一次看到那就讓我這個 Rust 菜鳥來解釋一下這兩個的差別是什麼吧!首先先來看一段官方說明,

Signed and unsigned refer to whether it’s possible for the number to be negative or positive—in other words, whether the number needs to have a sign with it (signed) or whether it will only ever be positive and can therefore be represented without a sign (unsigned).

因此簡單來說 i 的數字範圍為https://ithelp.ithome.com.tw/upload/images/20190923/20119807SxeDfb2MX0.png
而 u 的數字範圍為https://ithelp.ithome.com.tw/upload/images/20190923/20119807xVDKORtU88.png

舉例來說像是 i8 的數字範圍就是 -128 到 127,而 u8 的數字範圍則是 0 到 255,而 isize 和 usize 是根據你的系統位元決定,例如你是 32 位元的作業系統那麼 isize 就等同於 i32 而 usize 亦然。

另外是就我所寫過的程式中不論是 java、python 還是 javascript 都沒有這樣的 unsigned 的類型他的好處有幾點如下,

  • 確保在編譯期間一定是正數
  • 記憶體最佳化-當你需要保存比較大的正數而不需要負數值
let mut y: u8 = 0;
y = -1;
    ^^ cannot apply unary operator `-`

Note: 不可以將 unsigned 類型的變數賦予負數值

最後就是 overflow 的時候怎麼處理,這邊就直接附上官方的說明吧!

Integer Overflow
Let’s say you have a variable of type u8 that can hold values between 0 and 255. If you try to change the variable to a value outside of that range, such as 256, integer overflow will occur. Rust has some interesting rules involving this behavior. When you’re compiling in debug mode, Rust includes checks for integer overflow that cause your program to panic at runtime if this behavior occurs. Rust uses the term panicking when a program exits with an error; we’ll discuss panics in more depth in the “Unrecoverable Errors with panic!” section in Chapter 9.
When you’re compiling in release mode with the --release flag, Rust does not include checks for integer overflow that cause panics. Instead, if overflow occurs, Rust performs two’s complement wrapping. In short, values greater than the maximum value the type can hold “wrap around” to the minimum of the values the type can hold. In the case of a u8, 256 becomes 0, 257 becomes 1, and so on. The program won’t panic, but the variable will have a value that probably isn’t what you were expecting it to have. Relying on integer overflow’s wrapping behavior is considered an error. If you want to wrap explicitly, you can use the standard library type Wrapping.

大概翻譯一下就是在 runtime 時 overflow 的話 Rust 並不會讓程式掛掉但是你所運算出來的值可能會跟你預期的有所差別。

浮點數

浮點數就簡單許多一樣有分 32 位元或 64 位元的浮點數,但是不同位元的浮點數不能一起運算。

fn data_types_float() {
  // 浮點數型態
  // Rust’s floating-point types are f32 and f64
  // 64 位元跟 32 位元
  let x: f64 = 2.5;
  println!("The value of x is: {}", x);
  let y: f32 = 3.3;
  println!("The value of y is: {}", x + y);
                                      ^ no implementation for `f64 + f32`
}

Note: 不同位元的浮點數不能一起運算

官網根據浮點數的說明,

Floating-point numbers are represented according to the IEEE-754 standard. The f32 type is a single-precision float, and f64 has double precision.

數值運算

運算值的類型必須相同才能運算例如,

fn numeric_operations() {
  // 同樣的型態才可以做運算
  let sum = 5 + 10;
  println!("sum = {}", sum);                    // sum = 15
  
  let difference = 95.5 - 4.0;
  println!("difference = {}", difference);      // difference = 91.5
 
  let product = 4 * 30;
  println!("product = {}", product);            // product = 120
  
  let quotient = 56.7 / 32.2;
  println!("quotient = {}", quotient);          // quotient = 1.7608695652173911
  
  let remainder = 43 % 5;
  println!("remainder = {}", remainder);        // remainder = 3
}

布林

這也很簡單沒啥特別的地方偷懶一下就用示例帶過,

fn data_types_boolean() {
  // 布林型態
  let t = true;
  println!("t = {}", t);
  let f: bool = false; // with explicit type annotation
  println!("f = {}", f);
}

字串

字串同樣也很簡單沒啥特別的地方,

pub fn data_types_char() {
  // 字串型態
  let c = 'z';
  println!("c = {}", c);
  let z = 'ℤ';
  println!("z = {}", z);
  let heart_eyed_cat = '?';
  println!("heart_eyed_cat = {}", heart_eyed_cat);
  
  // 也可以這樣宣告
  let g = String::from("z");
  println!("g = {}", g);
  let a = String::from("ℤ");
  println!("a = {}", a);
  let p = String::from("?");
  println!("p = {}", p);
}

總結

除了 int 的類型稍微複雜一點之外其他的都跟一般的語言差不多 Rust 在編譯期間提供良好的型別檢查方式來確保開發者不會誤入歧途。下一篇將會繼續介紹還未介紹完的資料型態,讓我們繼續努力吧!

最後一樣有問題歡迎發問

/images/emoticon/emoticon07.gif

參考網址

rust_data_types

on-integer-types-in-rust


上一篇
[Day 7] Rust Variables and Mutability 變數和可變性
下一篇
[DAY 9] Rust Data Types 資料型態 (2)
系列文
WebAssembly + Rust 的前端應用30

尚未有邦友留言

立即登入留言