資料型態是程式語言中非常基本也非常重要的部份。我們今天就來學習 Rust 的基礎資料型態吧!
上一篇:quiz1
本系列主頁面:Rust繁中簡學!
繁體中文版 Rustlings:https://github.com/TimLai666/rustlings-zh-TW
安裝方法:00_intro
04_primitive_types(原始資料型態)
原始類型
Rust 有幾種基本類型是直接由編譯器實現的。在這一部分,我們將介紹最重要的那些。
進一步了解
README 幾乎什麼都沒說,關於 Rust 的資料型態,可以參考 繁體中文版《Rust 程式設計語言》 裡的講解。
練習一(primitive_types1.rs)
// primitive_types1.rs // // 填寫剩下缺少代碼的行!沒有提示,沒有技巧,只是讓您習慣打字 :) // I AM NOT DONE fn main() { // 布林值 (`bool`) let is_morning = true; if is_morning { println!("早安!"); } let is_evening = false; // 像例子一樣完成這行!或者讓它變成 false! if is_evening { println!("晚安!"); } }
✓ 成功運行 exercises/04_primitive_types/primitive_types1.rs! 🎉 🎉 代碼正在編譯! 🎉 🎉 輸出: ==================== 早安! ==================== 您可以繼續進行此練習, 或通過刪除 `I AM NOT DONE` 註釋來進入下一個練習: 3 | // 填寫剩下缺少代碼的行!沒有提示,沒有技巧,只是讓您習慣打字 :) 4 | 5 | // I AM NOT DONE 6 | 7 | fn main() {
這題什麼都沒做就通過編譯了,應該是使用 ChatGPT 翻譯的時候被它雞婆的做完了😅(GitHub 庫已修正)。
布林值的部分很簡單,以小寫 true
和 false
來表示,基本上跟 JavaScript 一樣。
那就直接跳下一題吧。
練習二(primitive_types2.rs)
// primitive_types2.rs // // 填寫剩下缺少代碼的行!沒有提示,沒有技巧,只是讓您習慣打字 :) // I AM NOT DONE fn main() { // 字符 (`char`) // 注意是 _單引號_,這與您經常看到的雙引號不同。 let my_first_initial = 'C'; if my_first_initial.is_alphabetic() { println!("字母!"); } else if my_first_initial.is_numeric() { println!("數字!"); } else { println!("既不是字母也不是數字!"); } let // 像例子一樣完成這行!您最喜歡的字符是什麼?試試字母、數字、特殊字符,或者不同語言的字符,甚至是表情符號! if your_character.is_alphabetic() { println!("字母!"); } else if your_character.is_numeric() { println!("數字!"); } else { println!("既不是字母也不是數字!"); } }
! 編譯 exercises/04_primitive_types/primitive_types2.rs 失敗!請再試一次。以下是輸出: error: expected identifier, found keyword `if` --> exercises/04_primitive_types/primitive_types2.rs:21:5 | 21 | if your_character.is_alphabetic() { | ^^ expected identifier, found keyword error: aborting due to 1 previous error
第二題一樣是打字題,只要在 let
那裡隨便設一個放字元的變數就行。
我就放 x
好了。
// primitive_types2.rs // // 填寫剩下缺少代碼的行!沒有提示,沒有技巧,只是讓您習慣打字 :) // I AM NOT DONE fn main() { // 字符 (`char`) // 注意是 _單引號_,這與您經常看到的雙引號不同。 let my_first_initial = 'C'; if my_first_initial.is_alphabetic() { println!("字母!"); } else if my_first_initial.is_numeric() { println!("數字!"); } else { println!("既不是字母也不是數字!"); } let your_character = 'x'; // 像例子一樣完成這行!您最喜歡的字符是什麼?試試字母、數字、特殊字符,或者不同語言的字符,甚至是表情符號! if your_character.is_alphabetic() { println!("字母!"); } else if your_character.is_numeric() { println!("數字!"); } else { println!("既不是字母也不是數字!"); } }
✓ 成功運行 exercises/04_primitive_types/primitive_types2.rs! 🎉 🎉 代碼正在編譯! 🎉 🎉 輸出: ==================== 字母! 字母! ==================== 您可以繼續進行此練習, 或通過刪除 `I AM NOT DONE` 註釋來進入下一個練習: 3 | // 填寫剩下缺少代碼的行!沒有提示,沒有技巧,只是讓您習慣打字 :) 4 | 5 | // I AM NOT DONE 6 | 7 | fn main() {
這裡有一點要注意!變數裡放字元必須使用單引號('
),不能使用雙引號("
),否則無法通過編譯。這點跟 C++ 比較像。
從程式碼中也可以看到,變數.is_alphabetic()
可以判斷一個字元是不是字母(這裡說的「字母」包括英文字母以外的文字,因為就算是英文以外的語言,他還是會判斷為 true
);變數.is_numeric()
則可以判斷該字元是不是數字(這裡的「數字」實際上還是「字元」的資料型態,跟 i32 的那種數字不一樣,要注意不能搞混了)。
練習三(primitive_types3.rs)
// primitive_types3.rs // // 創建一個包含至少 100 個元素的陣列,並填寫 ??? 處的代碼。 // // 執行 `rustlings hint primitive_types3` 或使用 `hint` watch 子命令來獲取提示。 // I AM NOT DONE fn main() { let a = ??? if a.len() >= 100 { println!("哇,那是一個大陣列!"); } else { println!("嗯,我早餐吃的就是這樣的陣列。"); panic!("陣列不夠大,需要更多元素") } }
! 編譯 exercises/04_primitive_types/primitive_types3.rs 失敗!請再試一次。以下是輸出: error: expected expression, found `?` --> exercises/04_primitive_types/primitive_types3.rs:10:13 | 10 | let a = ??? | ^ expected expression error: aborting due to 1 previous error
這題要創建一個陣列。我們先看看陣列是什麼:
陣列型別
另一種取得數個數值集合的方法是使用陣列。和元組不一樣的是,陣列中的每個型別必須是一樣的。和其他語言的陣列不同,Rust 的陣列是固定長度的。
我們將數值寫在陣列中的括號內,每個數值再用逗號區隔開來:
檔案名稱:src/main.rs
fn main() { let a = [1, 2, 3, 4, 5]; }當你想要你的資料被配置在堆疊(stack)而不是堆積(heap)的話,使用陣列是很好的選擇(我們會在第四章討論堆疊與堆積的內容)。或者當你想確定你永遠會取得固定長度的元素時也是。所以陣列不像向量(vector)型別那麼有彈性,向量是標準函式庫提供的集合型別,類似於陣列但允許變更長度大小。如果你不確定該用陣列或向量的話,通常你應該用向量就好。第八章將會討論更多向量的細節。
不過如果你知道元素的多寡不會變的話,陣列就是個不錯的選擇。舉例來說,如果你想在程式中使用月份的話,你可能就會選擇用陣列宣告,因為永遠只會有 12 個月份:
let months = ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"];要詮釋陣列型別的話,你可以在中括號寫出型別和元素個數,並用分號區隔開來,如以下所示:
let a: [i32; 5] = [1, 2, 3, 4, 5];
i32
在此是每個元素的型別,在分號後面的數字5
指的是此陣列有五個元素。如果你想建立的陣列中每個元素數值都一樣的話,你可以指定一個數值後加上分號,最後寫出元素個數。如以下所示:
let a = [3; 5];陣列
a
會包含5
個元素,然後每個元素的初始化數值均為3
。這樣寫與let a = [3, 3, 3, 3, 3];
的寫法一樣,但比較簡潔。獲取陣列元素
一個陣列是被配置在堆疊上且已知固定大小的一整塊記憶體,你可以用索引來取得陣列的元素,比如:
檔案名稱:src/main.rs
fn main() { let a = [1, 2, 3, 4, 5]; let first = a[0]; let second = a[1]; }在此範例中,變數
first
會得到數值1
,因為這是陣列索引[0]
的數值。變數second
則會從陣列索引[1]
得到數值2
。
重點大概是以下幾點:
- 陣列可以用
let 變數: [資料型態; 長度] = [元素]
或let 變數 = [初始值; 長度]
來宣告。 - 陣列用中括號(
[]
)表示,陣列裡每個元素用逗號隔開。 - Rust 陣列中的所有元素資料型態必須相同。
- Rust 的陣列長度固定,不能改變。
看起來非常難用呢!我們回到練習吧。
繼續練習三(primitive_types3.rs)
我們直接宣告一個長度為 101 、元素的值為 0 陣列。
// primitive_types3.rs // // 創建一個包含至少 100 個元素的陣列,並填寫 ??? 處的代碼。 // // 執行 `rustlings hint primitive_types3` 或使用 `hint` watch 子命令來獲取提示。 // I AM NOT DONE fn main() { let a = [0; 101]; if a.len() >= 100 { println!("哇,那是一個大陣列!"); } else { println!("嗯,我早餐吃的就是這樣的陣列。"); panic!("陣列不夠大,需要更多元素") } }
✓ 成功運行 exercises/04_primitive_types/primitive_types3.rs! 🎉 🎉 代碼正在編譯! 🎉 🎉 輸出: ==================== 哇,那是一個大陣列! ==================== 您可以繼續進行此練習, 或通過刪除 `I AM NOT DONE` 註釋來進入下一個練習: 5 | // 執行 `rustlings hint primitive_types3` 或使用 `hint` watch 子命令來獲取提示。 6 | 7 | // I AM NOT DONE 8 | 9 | fn main() {
練習四(primitive_types4.rs)
// primitive_types4.rs // // 從陣列 a 中獲取一個切片,填寫 ??? 處的代碼,使測試通過。 // // 執行 `rustlings hint primitive_types4` 或使用 `hint` watch 子命令來獲取提示。 // I AM NOT DONE #[test] fn slice_out_of_array() { let a = [1, 2, 3, 4, 5]; let nice_slice = ??? assert_eq!([2, 3, 4], nice_slice) }
! 編譯 exercises/04_primitive_types/primitive_types4.rs 失敗!請再試一次。以下是輸出: error: expected expression, found `?` --> exercises/04_primitive_types/primitive_types4.rs:13:22 | 13 | let nice_slice = ??? | ^ expected expression error: aborting due to 1 previous error
這題要從陣列中取出一個元素為 2, 3 , 4
「切片」。切片的寫法是 &陣列名稱[開始(含)的索引..結束(不含)的索引]
。
由於索引是從 0 開始計算,所以這題應該要寫成 &a[1..4]
。
// primitive_types4.rs // // 從陣列 a 中獲取一個切片,填寫 ??? 處的代碼,使測試通過。 // // 執行 `rustlings hint primitive_types4` 或使用 `hint` watch 子命令來獲取提示。 // I AM NOT DONE #[test] fn slice_out_of_array() { let a = [1, 2, 3, 4, 5]; let nice_slice = &a[1..4]; assert_eq!([2, 3, 4], nice_slice) }
✓ 成功測試 exercises/04_primitive_types/primitive_types4.rs! 🎉 🎉 代碼正在編譯,並且測試通過! 🎉 🎉 您可以繼續進行此練習, 或通過刪除 `I AM NOT DONE` 註釋來進入下一個練習: 5 | // 執行 `rustlings hint primitive_types4` 或使用 `hint` watch 子命令來獲取提示。 6 | 7 | // I AM NOT DONE 8 | 9 | #[test]
練習五(primitive_types5.rs)
// primitive_types5.rs // // 解構 `cat` 元組,使 println 可以正常工作。 // // 執行 `rustlings hint primitive_types5` 或使用 `hint` watch 子命令來獲取提示。 // I AM NOT DONE fn main() { let cat = ("Furry McFurson", 3.5); let /* 在此填寫解構模式 */ = cat; println!("{} is {} years old.", name, age); }
! 編譯 exercises/04_primitive_types/primitive_types5.rs 失敗!請再試一次。以下是輸出: error: expected pattern, found `=` --> exercises/04_primitive_types/primitive_types5.rs:11:24 | 11 | let /* 在此填寫解構模式 */ = cat; | ^ expected pattern error: aborting due to 1 previous error
這題使用到了 元組。
元組(Tuple)型別
元組是個將許多不同型別的數值合成一個複合型別的常見方法。元組擁有固定長度:一旦宣告好後,它們就無法增長或縮減。
我們建立一個元組的方法是寫一個用括號囊括起來的數值列表,每個值再用逗號分隔開來。元組的每一格都是一個獨立型別,不同數值不必是相同型別。以下範例我們也加上了型別詮釋,平時不一定要加上:
檔案名稱:src/main.rs
fn main() { let tup: (i32, f64, u8) = (500, 6.4, 1); }此變數
tup
就是整個元組,因為一個元組就被視為單一複合元素。要拿到元組中的每個獨立數值的話,我們可以用模式配對(pattern matching)來解構一個元組的數值,如以下所示:檔案名稱:src/main.rs
fn main() { let tup = (500, 6.4, 1); let (x, y, z) = tup; println!("y 的數值為:{y}"); }此程式先是建立了一個元組然後賦值給
tup
,接著它用模式配對和let
將tup
拆成三個個別的變數x
、y
和z
。這就叫做解構(destructuring),因為它將單一元組拆成了三個部分。最後程式將y
的值印出來,也就是6.4
。我們也可以直接用句號(
.
)再加上數值的索引來取得元組內的元素。舉例來說:檔案名稱:src/main.rs
fn main() { let x: (i32, f64, u8) = (500, 6.4, 1); let five_hundred = x.0; let six_point_four = x.1; let one = x.2; }此程式建立了元組
x
,然後用它們個別的索引來存取元組的元素。和多數程式語言一樣,元組的第一個索引是 0。沒有任何數值的元組有一種特殊的名稱叫做單元型別(Unit),其數值與型別都寫作
()
,通常代表一個空的數值或空的回傳型別。表達式要是沒有回傳任何數值的話,它們就會隱式回傳單元型別。
回到練習五(primitive_types5.rs)
這題的解構式只要用一對小括號,裡面放 name
和 age
,然後等於 cat
就行。
// primitive_types5.rs // // 解構 `cat` 元組,使 println 可以正常工作。 // // 執行 `rustlings hint primitive_types5` 或使用 `hint` watch 子命令來獲取提示。 // I AM NOT DONE fn main() { let cat = ("Furry McFurson", 3.5); let (name, age) = cat; println!("{} is {} years old.", name, age); }
✓ 成功運行 exercises/04_primitive_types/primitive_types5.rs! 🎉 🎉 代碼正在編譯! 🎉 🎉 輸出: ==================== Furry McFurson is 3.5 years old. ==================== 您可以繼續進行此練習, 或通過刪除 `I AM NOT DONE` 註釋來進入下一個練習: 5 | // 執行 `rustlings hint primitive_types5` 或使用 `hint` watch 子命令來獲取提示。 6 | 7 | // I AM NOT DONE 8 | 9 | fn main() {
練習六(primitive_types6.rs)
// primitive_types6.rs // // 使用元組索引來訪問 `numbers` 的第二個元素。您可以將表達式替換到 ??? 處,使測試通過。 // // 執行 `rustlings hint primitive_types6` 或使用 `hint` watch 子命令來獲取提示。 // I AM NOT DONE #[test] fn indexing_tuple() { let numbers = (1, 2, 3); // 使用元組索引語法替換下面的 ???。 let second = ???; assert_eq!(2, second, "這不是元組中的第二個數字!") }
! 編譯 exercises/04_primitive_types/primitive_types6.rs 失敗!請再試一次。以下是輸出: error: expected expression, found `?` --> exercises/04_primitive_types/primitive_types6.rs:13:18 | 13 | let second = ???; | ^ expected expression error: aborting due to 1 previous error
這題一樣是在練習使用元組。
元組的索引很簡單:元組名稱.索引
。
// primitive_types6.rs // // 使用元組索引來訪問 `numbers` 的第二個元素。您可以將表達式替換到 ??? 處,使測試通過。 // // 執行 `rustlings hint primitive_types6` 或使用 `hint` watch 子命令來獲取提示。 // I AM NOT DONE #[test] fn indexing_tuple() { let numbers = (1, 2, 3); // 使用元組索引語法替換下面的 ???。 let second = numbers.1; assert_eq!(2, second, "這不是元組中的第二個數字!") }
✓ 成功測試 exercises/04_primitive_types/primitive_types6.rs! 🎉 🎉 代碼正在編譯,並且測試通過! 🎉 🎉 您可以繼續進行此練習, 或通過刪除 `I AM NOT DONE` 註釋來進入下一個練習: 5 | // 執行 `rustlings hint primitive_types6` 或使用 `hint` watch 子命令來獲取提示。 6 | 7 | // I AM NOT DONE 8 | 9 | #[test]
總結
我們今天認識了 Rust 的布林值、字元、陣列、切片和元組。它們各自的重點如下:
- 布林值
- 以小寫
true
和false
來表示是否為真。
- 以小寫
- 字元
- 必須使用單引號(
'
),不能使用雙引號("
)。 變數.is_alphabetic()
可以判斷一個字元是不是字母;變數.is_numeric()
可以判斷該字元是不是數字。
- 必須使用單引號(
- 陣列
- 陣列可以用
let 變數: [資料型態; 長度] = [元素]
或let 變數 = [初始值; 長度]
來宣告。 - 陣列用中括號(
[]
)表示,陣列裡每個元素用逗號隔開。 - Rust 陣列中的所有元素資料型態必須相同。
- Rust 的陣列長度固定,不能改變。
- 陣列可以用
- 切片
&陣列名稱[開始(含)的索引..結束(不含)的索引]
。
- 元組
- 元素可以是不同資料型態。
- 長度固定,不可改變。
- 可以用模式配對和
let
解構。
這個單元真的是大雜燴,用到一堆重要觀念 README 卻都不寫。Rustlings 在概念解說的部分實在有待加強。
One Comment