在 Rust 中,把值指定給一個名稱稱為_綁定_。除非用mut關鍵字宣告,否則綁定都是不可變的。由於 Rust 是靜態型別的語言,每個綁定都有一個在編譯期就知道的型別。
綁定最常使用let關鍵字定義。對大多數綁定來說,指定型別是選填的,因為 Rust 的_型別推論_通常可以根據值推斷出型別。一個綁定看起來像這樣:
// Automatically inferred type
let fingers = 10;
函式是_項目_。綁定通常指向某個特定的值,而項目指的是程式碼組織的單位,通常是一個函式或一個模組,它在程式的整個生命週期內都可以使用。函式會自動回傳最後一個運算式的結果。函式可以有 0 個或多個參數,這些參數是生命週期僅限於該次函式呼叫的綁定。
理論上函式也能做型別推論,但 Rust 刻意在語言設計上關閉了這個功能。這表示寫程式時,你得花多一點時間明確指定函式的輸入與輸出型別;但讀程式碼時就省下了時間,因為所有輸入與輸出型別都已經明確寫出來了。
fn add(x: i32, y: i32) -> i32 {
x + y
}
呼叫函式的方式,是寫出函式名稱,後面接著括號。如果函式需要參數,就必須在括號內為每個參數指定一個引數。
let five = add(2, 3);
如果無法推斷綁定的型別,編譯器會回報錯誤。要修正這個問題,請為該綁定加上明確的型別標註。
// Explicit type annotation
let fingers: i32 = 10;
Rust 的項目在定義之前或之後都可以使用,因為它們有靜態生命週期。相較之下,綁定只能在定義_之後_使用。在綁定定義之前就使用它,會導致編譯錯誤。
fn main() {
// `fn add` hasn't yet been defined, but that's perfectly ok
dbg!(add(3, 4));
}
fn add(x: i32, y: i32) -> i32 {
x + y
}
// this won't compile; `a` is used before its binding is defined
let b = a;
let a = x + y;
Rust 使用大括號({})來定義作用域。在作用域內定義的綁定無法脫離這個作用域。
let a = 1;
dbg!(a); // 1
{
// Here, we re-bind `a` to a new value, which is still immutable.
// This technique is called _shadowing_. The new binding is constrained to
// this anonymous scope. Outside this scope, the previous binding still
// applies.
let a = 2;
let b = 3;
dbg!(a, b); // 2, 3
}
// can't use `b` anymore because it is out of scope
// dbg!(b);
// The shadowed `a` in the inner scope above has fallen out of scope,
// leaving us with our original binding.
dbg!(a); // 1
Rust 的項目通常會用模組來組織。每個 crate 本身隱含就是一個模組,但它可以定義任意深度的內部子模組。模組會把相關的功能群組在一起,並使用mod關鍵字定義。
mod calc_i32 {
fn add(a: i32, b: i32) -> i32 { a + b }
fn sub(a: i32, b: i32) -> i32 { a - b }
fn mul(a: i32, b: i32) -> i32 { a * b }
fn div(a: i32, b: i32) -> i32 { a / b }
}
Rust 支援兩種註解。//這個關鍵字代表單行註解;從它之後到行尾的所有內容都會被忽略。/*和*/這兩個關鍵字代表多行註解;在這兩個關鍵字之間的所有內容都會被忽略。偏好使用單行註解是符合慣例的好做法。
Rust 也支援文件註解,它們會出現在cargo doc產生的文件中。外層文件註解使用///關鍵字撰寫,行為和//關鍵字完全相同。它們會套用在後面接著的項目上,例如函式:
/// The `add` function produces the sum of its arguments.
fn add(x: i32, y: i32) -> i32 { x + y }
內層文件註解使用//!關鍵字撰寫,行為和//關鍵字完全相同。它們會套用在包住它們的項目上,例如模組:
mod my_cool_module {
//! This module is the bee's knees.
}
文件註解可以是任意長度,並且可以包含 Markdown,這些內容會算繪進產生的文件裡。
在這個練習中,你會寫一些程式碼,幫助自己照著最愛的食譜書烤出超美味的千層麵。
你有四個任務,全都和烹調千層麵所花的時間有關。
定義expected_minutes_in_oven綁定,用來確認千層麵應該在烤箱中烤幾分鐘。根據食譜書,預期的烤箱時間是 40 分鐘:
expected_minutes_in_oven()
// Returns: 40
定義remaining_minutes_in_oven函式,它接受千層麵已經在烤箱中實際烤過的分鐘數作為參數,並根據上一個任務算出的預期烤箱時間,回傳千層麵還需要在烤箱中待幾分鐘。
remaining_minutes_in_oven(30)
// Returns: 10
定義preparation_time_in_minutes函式,它接受你加到千層麵裡的層數作為參數,並回傳你準備千層麵所花的分鐘數,假設每一層需要 2 分鐘來準備。
preparation_time_in_minutes(2)
// Returns: 4
定義elapsed_time_in_minutes函式,它接受兩個參數:第一個參數是你加到千層麵裡的層數,第二個參數是千層麵已經在烤箱中的分鐘數。這個函式應該回傳你烹調千層麵所花的分鐘數,也就是準備時間(分鐘)加上千層麵目前在烤箱中已經待的分鐘數。
elapsed_time_in_minutes(3, 20)
// Returns: 26