在 stack overflow 的 2024 年調查中, Rust 被列為最讚賞的語言之一
Rust 的高效能特性,讓許多的工具都掀起以 Rust 的改寫風潮
這次我們會用 Rust 來做一個工具,讓大家體驗用 Rust 開發的樂趣
事前準備
- 安裝 Rust
- 可參考專案成果
step 0. 用 Cargo 新增專案
Cargo 是 Rust 的套件管理工具,我們可以用 Cargo 來新增專案,也可以用它來編譯 Rust 檔案以及執行
- 新增專案
1 | cargo new password_generator |
當我們要編譯 Rust 專案時,就可以下該指令:
1 | cargo build |
但如果是編譯加上執行專案的話,就可以改以下指令:
1 | cargo run |
Step 1. 打造 CLI
首先,我們要來打造 CLI ,來創造使用者與程式互動的介面
我們會用一個套件,叫做 inquire
來做出 CLI
第一次加套件,就先用手動的方式來加入吧!
Cargo.toml 檔案中不只是放置檔案的基本資料,也會放套件的資訊哦!
1 | # Cargo.toml |
這個套件有支援很多種輸入方式,有包含以下:
- 文字輸入
- 日期選擇
- 選擇題
- 複選題
有興趣的話可直接進入到該套件的 github 看看
接著我們來設定密碼的長度
Step 2 - 1. 設定密碼長度
我們要先到 main.rs
檔案中,
main.rs
每一個 Rust 專案都必須要有 main.rs 檔案,
我們可以把 main.rs 當作是一個進入點,
main.rs 有幾個特性
- 沒有參數
- 不會有 return 值
介紹完 main.rs 後,我們就來設定密碼長度吧!
由使用者自行決定密碼長度相對彈性,
因此我們會使用套件的文字輸入,讓使用者自行輸入需要的密碼長度
在使用 Text 模組前,我們要在檔案上面使用 use
來載入該套件的 Text 模組,並且做出文字輸入的問題
1 | # main.rs |
定義變數
這邊要跟大家提一下,在 Rust 中的變數定義需要用 let 或者 const
1 | let number_one = “1” |
跟 JavaScript 一樣, let 是可以做更改的, const 是不能做更改的
如果要讓該變數的值可以更改,我們就必須這樣用
1 | let mut number = “1234” |
Error Handling
我們在使用 Text::new
模組中的 prompt
方法時,會回傳一個 Result
型態的值
Result
是 Error Handling 的一種
當我們需要判斷程式結果時,就可以使用 Error Handling
Error Handling
分為 可復原的
以及 不可復原的
可復原的為 Result
與 Option
Result<T, E>
來表示結果是成功還是失敗,需要兩個參數,一個是 T,一個是 E,其實這兩個參數是
T 代表著成功後回傳的變數
E 則是失敗回傳的錯誤訊息
來個小範例:
1 | fn main() { |
Option<T>
適用於此變數或者結果是否存在,會有 None 以及 Some 這兩個結果
這邊的 T 跟 Result 不太一樣,可能會是 Some 或者是 None,不過他們都是參數
Some(c) 表示存在,並且回傳 c 變數(不一定要叫 c ,想叫什麼都可)
None 表示不存在
來個小範例:
1 | fn main() { |
所以他們用法上會有一些差別
接著我們來介紹在 Rust 中要怎麼把東西印出來
如果是要印一般的字串,其實蠻簡單的,用下方寫法就可以了
1 | println!("我是 Rust 我最棒!") |
但如果是要印出變數,我們就必須使用 {:?}
,來看一下用法
1 | let language = "Rust"; |
1 | let language = ["Rust", "Ruby"] |
用文字輸入做出密碼長度的問題後,
我們還要再去定義密碼長度的變數,
因為使用 Text::new("請問您的密碼長度要設定多少?").prompt()
做出來的會是一個 Result
,
之後要產生密碼是需要抓數字來判斷長度要多少,
所以我們可以要另外定義
( 預設可給 8,如果使用者輸入的值不是 8 ,就得改變,所以要給 mut
)
1 | fn main() { |
到這邊,我們就把密碼長度設定好了!
大家可以在終端機下 cargo run
來試試是否跳出 CLI
如果成功的話,我們就進行下一步吧!來判斷使用者所輸入的答案
Step 2 - 2. 設定密碼長度 - 判斷輸入的文字
在 Rust 中,判斷變數符合哪個情況,可以使用 match
match
類似 JavaScript 中的 switch
會依照值符合哪個狀況來執行不同的程式碼
假設 length_ans
是沒有錯誤的,那我們就去解析是否為數字
如果是數字,那就顯示設定的密碼長度為多少,並且將 length 重新賦值
如果不符合數字,那就會顯示 您輸入的不是有效的整數
1 | fn main() { |
設定完後,就可以輸入 cargo run
來試試執行結果!
Step 3. 定義密碼範本變數
我們會從密碼範本中產出隨機碼做成密碼,
由於密碼範本可能會更改,所以我們要讓他是 mut
1 | fn main() { |
Step 4 - 1. 是否包含數字 - 定義密碼的數字範本變數
在判斷是否包含數字以前,我們先將數字的密碼範本定義好
1 | fn main() { |
&str
在這邊我們看到了一個陌生的型態 &str
它叫做字串切片,顧名思義就是從字串切下來的一段字
要注意的是,他與字串是不一樣的喔
字串切片是不能被更改的
Step 4 - 2. 是否包含數字 - 密碼是否包含數字
這次我們會用選擇題來讓使用者回答,密碼是否要包含數字
所以我們會使用到 Confirm
模組
with_default
可以讓你建立預設值,假設使用者直接按 enter
下去,他就會直接帶入預設值
1 | use inquire::{Confirm, Text}; |
Step 4 - 3. 是否包含數字 - 判斷輸入的答案
一樣我們會用 match
來判斷狀況,並且透過 Result
來判斷程式碼是否有誤
假設沒有錯誤,就會印出 你的密碼將包含數字
且將 數字範本
塞入 密碼範本
中
1 | fn main() { |
Step 5 - 1. 密碼是否包含字元 / 符號 - 定義密碼的字元 / 符號範本變數
在設定是否要讓密碼包含字元 / 符號前,
我們也先來定義 字元 / 符號 的密碼範本
1 | fn main() { |
Step 5 - 2. 密碼是否包含字元 / 符號 - 複選題
接著我們會讓使用者用複選的方式來看是否要包含大小寫英文字母 還是 符號
我們會需要用到 MultiSelect
模組
1 | use inquire::{Text, Confirm, MultiSelect}; |
Vector
Vector 是一種存放資料的集合型別,在 Rust 中是屬於標準函式庫型別,
放置在同一個 Vector 裡面的資料都是要一樣型態的
1 | fn main() { |
Step 5 - 3. 密碼是否包含字元 / 符號 - 判斷輸入的答案
一樣是透過 match
來判斷 char_ans
比較特別的是, choice
會是一個 Vector
,因此我們需要用 for ... in
將裡面的答案一個個拿出來比對,
如果有 match 到大寫英文,我們就將大寫英文範本塞進去密碼範本中
match 選項最後有個 _
,這表示如果沒有到 match 上述的任何一個,就會走這條
1 | fn main() { |
Step 6 - 1. 產生隨機碼 - 安裝套件
接著我們需要用密碼範本來隨機產生
所以我們會需要用到 Rand
這個套件,
這個套件可以讓我們隨機產生數字,
所以直接下 cargo add rand
就會自動在 Cargo.toml 中加上 rand = "0.8.5"
了
Step 6 - 2. 產生隨機碼 - 產出密碼
這邊我們會使用到 rand 的 Rng 模組,
我們會透過 rand::thread_rng()
來產生隨機數字
會先將密碼範本中的每個字或者數字轉成 u8 格式,並且集合成一個陣列,char_bytes
會指向這個陣列
接著我們會由 0 ~ 密碼長度依序隨機抓出一個 index
最後在 char_byte
取出該 index 的值,並且轉為 字元
格式
最後將他們集合起來成為密碼
1 | use rand::Rng; |
u8
為正數的數字,不包含負數,u 是指 unsigned
, 8 則是指 8-bit
字元
我們可以定義任何符號,甚至是數字為字元,型別設定為 char 即可
但只能放一個字,後面務必要用單引號
1 | fn main() { |
Step 6 - 3. 包成方法
待會要去判斷密碼中是否包含數字(常出現結果沒有數字),所以隨機產生密碼的方法會重複使用
所以就來把他包成一個方法,記得要把它放在 main 方法外面,並且回傳為一個 String
字串格式
而剛剛放在 main 方法中的隨機產生碼就可以先刪除了
1 | fn generate_password(charset: &str, length: i32) -> String { |
function
當 function 會需要參數的時候,必須要將型別定義清楚,就像上方的 charset: &str
/ length: i32
一樣,
如果有回傳值,也必須要定義好型態,以上方例子來說,會回傳一個 String 型態
Step 7 - 1. 密碼未包含數字,就重來
沒有說要包含數字的話,我們就直接將產生密碼,不需要再做檢查
1 | fn main() { |
接著必須先判斷使用者有沒有說要包含數字
所以一樣用 match
與 Result
去判斷
1 | fn main() { |
如果有包含數字的話,就必須要去跑 loop 檢查是否有數字,沒有就重新產生,並且重新賦值
1 | fn main() { |
以上就完成密碼產生器囉!