這不就更新了嗎
該放起司的地方放起司,該放肉片的地方放肉片,再用左右大括號括起來,美味的 struct 完成了 (?
Photo by Pablo Merchán Montes on Unsplash |
但是 tuple 要存取成員時只能靠數字去索引,而 struct 多了類似 key 的方式去索引
有寫過 C/C++ 的大概都不陌生 struct
struct 就是可以有多個欄位 (fields),每個欄位有各自的型別和名字
struct User { // C++ string username; string email; unsigned int sign_in_count; bool active; };
struct User { // Rust username: String, email: String, sign_in_count: u64, active: bool, }
從上方可以發現主要是寫法上需要注意哪些要 ' , ' 哪些要 ' ; '
至於 Rust 最後一行為何還需要 ' , ' 呢?
這其實在 C99 的 designated initializer 也是如此
jserv 在 你所不知道的 C 語言:技巧篇 (2017-03-20) 33m32s 時有提到
除了所說的 macro 外,另外還有方便產生 C 的程式碼等其他好處 [參考]
宣告變數
注意 key: value 是用 ' : ' 給值而不是 ' = 'let user1 = User { email: String::from("someone@example.com"), username: String::from("someusername123"), active: true, sign_in_count: 1, };
可變性
若要成員可變,則要用 mut 關鍵字,但注意不是放在成員,而是整個變數 (user1)let mut user1 = User { email: String::from("someone@example.com"), username: String::from("someusername123"), active: true, sign_in_count: 1, }; user1.email = String::from("anotheremail@example.com");
函數回傳值
應該還記得函數最後一行為回傳值吧,放一個 struct 的實體就會回傳這個實體注意 email 和 username 的用法兩種皆可
fn build_user(email: String, username: String) -> User { User { email, username: username, active: true, sign_in_count: 1, } }
struct tuple
struct 跟 tuple 可以合用,比 tuple 多了型別名,比 struct 少了成員名struct Color(i32, i32, i32); struct Point(i32, i32, i32); let black = Color(0, 0, 0); let origin = Point(0, 0, 0);
輸出到螢幕
struct 也可以使用 println! 這個 macro 來輸出,不過要小改造一下1. 必須在要輸出的 struct 上方加上 #[derive(Debug)]
2. println! macro { } 中要加上 :? 或 :#? 詳細參數請參考
#[derive(Debug)] struct Rectangle { width: u32, height: u32, } fn main() { let rect1 = Rectangle { width: 30, height: 50 }; println!("rect1 is {:?}", rect1); println!("rect1 is {:#?}", rect1); }結果如下:
rect1 is Rectangle { width: 30, height: 50 } rect1 is Rectangle { width: 30, height: 50 }
另外還有 fmt 函式可以用,與 Java 中覆寫 toString() 有點像
但是 Rust 沒有繼承的概念,是以實作 std::fmt::Display 這個 Trait 來達成
最簡易的寫法如下,發現可以直接透過 println! 輸出了
( Trait 在之後應該會再出現
struct Rectangle { width: u32, height: u32, } impl std::fmt::Display for Rectangle { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "width: {}, height: {}", self.width, self.height) } } fn main() { let rect1 = Rectangle { width: 30, height: 50 }; println!("rect1 is {}", rect1); }
rect1 is width: 30, height: 50
方法 (Method)
方法簡單來說就是 struct 的函式struct 裡面記錄著結構的成員,而這些成員通常是變數,方法並不包含在裡面
不像 C++、Java 直接把方法定義在類別裡面,Rust 是使用實作的方式來達成
關鍵字是 impl
第一個參數是 self 就是實體 (instance) 方法,所謂實體方法就是可以用 ' . ' 去呼叫函數
例如定義一個回傳長方形面積的方法:
struct Rectangle { width: u32, height: u32, } impl Rectangle { fn area(&self) -> u32 { self.width * self.height } } fn main() { let rect1 = Rectangle { width: 30, height: 50 }; println!("area is {}", rect1.area()); }
self
這個第一個參數 self 很偉大它讓方法呼叫時同時決定是要 move (self)、borrow (&self)、mut borrow (&mut self) 的方式使用實體
例如改一下上面範例
impl Rectangle { fn area(self) -> u32 { // 把 & 拿掉 self.width * self.height } }這樣就會產生錯誤,因為第一次呼叫 area() 時就把 rect1 move 到方法中了
let rect1 = Rectangle { width: 30, height: 50 }; println!("area is {}", rect1.area()); println!("area is {}", rect1.area()); // error
一般 &self 最常用,若要更改內部成員的話就用 &mut self
struct Rectangle { width: u32, height: u32, } impl Rectangle { fn area(&self) -> u32 { self.width * self.height } fn set_width(&mut self, width: u32) { self.width = width; } } fn main() { let rect1 = Rectangle { width: 30, height: 50 }; println!("area is {}", rect1.area()); let mut rect1 = rect1; // 注意要先變成 mut 才可以被修改 rect1.set_width(40); println!("area is {}", rect1.area()); }
associated functions
沒有用 self 參數的就是 associated functions有點像是 java static 函數:類別方法
使用時就不是用 ' . ' 了,而是用 ' :: ',String 的 from 就是
通常是用來回傳一個實體
struct Rectangle { width: u32, height: u32, } impl Rectangle { fn new(width: u32, height: u32) -> Rectangle { // associated functions Rectangle { width, height } } fn area(&self) -> u32 { self.width * self.height } } fn main() { let rect1 = Rectangle::new(30, 50); println!("area is {}", rect1.area()); }
automatic referencing and dereferencing
這是 Rust 中為什麼沒有像 C/C++ 的 ' -> ' 去使用 borrow 過的成員或方法因為 Rust 會自動加上 &、&mut、 * 所以統一用 ' . ' 即可,超方便程式碼也比較乾淨
struct Rectangle { width: u32, height: u32, } impl Rectangle { fn new(width: u32, height: u32) -> Rectangle { Rectangle { width, height } } fn area(&self) -> u32 { self.width * self.height } } fn main() { let rect1 = &&&&Rectangle::new(30, 50); println!("area is {}", (****rect1).area()); println!("area is {}", rect1.area()); }
多重 impl 區塊
最後,impl 區塊並不限於只能用一個也就是可以對同一個 struct 定義不同的 impl
struct Rectangle { width: u32, height: u32, } impl Rectangle { fn new(width: u32, height: u32) -> Rectangle { Rectangle { width, height } } }
impl Rectangle { fn area(&self) -> u32 { self.width * self.height } } fn main() { let rect1 = Rectangle::new(30, 50); println!("area is {}", rect1.area()); }
參考資料:
Using Structs to Structure Related Data
沒有留言:
張貼留言