前言
本系列文書寫我個人初探 Rust 的學習筆記,章節劃分主要基於著名的 The Book,The Rust Programming Language,程式碼部分通常是個人閱讀消化後以方便說明的方式撰寫,完整學習建議直接參見該書。
The Rust Programming Language
該書也有中文翻譯版,不過個人閱讀以英文原版為主以鞏固對 terminology 的一致認識,我認為對未來閱讀以及查找資料會較為順暢。
不論語言該書都是相當優秀的學習資源,選擇適合你的語言開始學習 Rust 吧。
Appendix F: Translations of the Book
特徵 Traits
特徵用於定義共同行為,類似於某些語言中 interface 的用途,但有些不同。
定義
定義 trait 的寫法如下:
trait Greeting {
fn say_hello(&self) -> String;
}
和 impl 區塊非常像,不過這邊不寫方法的實作,而是提供簽名 (signatures),定義方法的名字、參數與回傳值。
實作
這邊是為 struct 實作 trait 的例子,在 impl 後面加上 for 以實作特徵。
trait Greeting {
fn say_hello(&self) -> String;
}
struct Person {
name: String
}
impl Greeting for Person {
fn say_hello(&self) -> String {
format!("Hello {}!", self.name)
}
}
fn main() {
let p1 = Person {
name: String::from("Josh")
};
println!("{}", p1.say_hello()); // Hello Josh!
}
要注意的是實作必須包含所有 trait 所定義的簽名,否則會出現錯誤。
trait Greeting {
fn say_hello(&self) -> String;
fn say_goodbye(&self) -> String; // added
}
struct Person {
name: String
}
impl Greeting for Person {
fn say_hello(&self) -> String {
format!("Hello {}!", self.name)
}
}
// ERROR: not all trait items implemented, missing: `say_bye`
預設實作
在 trait block 中並非不能撰寫實作部分,寫在 trait 中的實作會被用為預設實作。
trait Greeting {
fn say_hello(&self) -> String;
fn say_goodbye(&self) -> String {
String::from("Good bye!")
}
}
struct Person {
name: String
}
impl Greeting for Person {
fn say_hello(&self) -> String {
format!("Hello {}!", self.name)
}
}
fn main() {
let p1 = Person {
name: String::from("Josh")
};
println!("{}", p1.say_goodbye()); // Good bye!
}
用 trait 定參數
我們可以利用 trait 來定義參數的型別為有實作特定 trait 的類型,寫為 impl Trait 如下。
fn print_hello(p: &impl Greeting) {
println!("{}", p.say_hello());
}
上面可以改用 trait bound 的寫法寫成下面這樣,這在介紹到泛型時用過。
fn print_hello<T: Greeting>(p: &T) {
println!("{}", p.say_hello());
}
進階使用
- 可以使用
+來指定複數個 trait - 使用
where語法定義 trait bound,避免冗長的泛型定義
下面程式碼建立了兩個 struct,其中 Person 實作了 Hello 和 Goodbye trait,P 則只實作 Hello,利用上面兩個特性定義 greeting 函式。
struct Person {
name: String
}
struct P {
name: String
}
trait Hello {
fn say_hello(&self) -> String {
String::from("Hello!")
}
}
trait Goodbye {
fn say_goodbye(&self) -> String {
String::from("Good bye!")
}
}
impl Hello for Person {}
impl Goodbye for Person {}
impl Hello for P {}
fn greeting<T, U>(p1: &T, p2: &U)
where
T: Hello + Goodbye,
U: Hello
{
println!("{}", p1.say_hello());
println!("{}", p1.say_goodbye());
println!("{}", p2.say_hello());
}
fn main() {
let p1 = Person {
name: String::from("Josh")
};
let p2 = P {
name: String::from("Josh")
};
greeting(&p1, &p2);
// output:
// Hello!
// Good bye!
// Hello!
}
對所有有符合特徵的類型實作特徵
下面程式為有實作 Hello 的類型實作 PrintHello trait。
struct Person {
name: String
}
trait Hello {
fn say_hello(&self) -> String {
String::from("Hello!")
}
}
trait PrintHello {
fn print_hello(&self);
}
impl Hello for Person {}
impl<T: Hello> PrintHello for T {
fn print_hello(&self){
println!("{}", self.say_hello());
}
}
fn main() {
let p1 = Person {
name: String::from("Josh")
};
p1.print_hello(); // Hello!
}
寫在最後
泛型與特徵結合使用的技巧很多,這邊只有截選一些個人的筆記,掌握兩者並結合使用是減少程式碼重複的關鍵,也讓別人更易於使用自己寫的程式。
下一章是 Rust 中非常重要也非常不容易的 topic,生命週期 (lifetime)。