【Rust-book】第五章 使用結構體來組織相關聯的資料
結構,或者結構體,是一種自定義資料型別,它允許我們命名多個相關的值並將它們組成一個有機的結合體。
可以把結構體視作物件中的資料屬性
1 對比元組和結構體之間的異同,並演示如何使用結構體
2 討論如何定義方法和關聯函式,他們可以指定那些與結構體資料相關的行為
結構體和列舉體是用來建立型別的基本工具,在特定領域中的新型別同樣可以享受到Rust編譯時型別檢查系統的所有優勢。
定義並例項化結構體
結構體需要給每個資料賦予名字,可以清楚地表明它們地意義。
由於欄位命名,不再需要依賴順序索引來指定或訪問例項中的值。
關鍵字struct被用來定義並命名結構體,一個良好的結構體名稱應當能反映出自身資料組合的意義。在隨後的花括號中宣告所有資料的名字及型別,這些資料被稱為欄位。
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
在為每個欄位賦予具體的值來建立結構體例項,賦值的順序不需要嚴格對應在結構體中宣告它們的順序。
結構體的定義就像型別的通用模版一樣,當我們將具體的資料填入模板時就創建出了新的例項。
let user1 = User { email: String::from("[email protected]"), username: String::from("someusername123"), active: true, sign_in_count: 1, };
可以通過點號來訪問例項中的特定欄位。
let mut user1 = User {
email: String::from("[email protected]"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
user1.email = String::from("[email protected]");
一旦例項可變,那麼例項中的所有欄位都將是可變的。
Rust不允許我們將單獨宣告某一部分欄位的可變性。
fn build_user( email: String, username: String) -> User { User { // 在變數名與欄位名相同時使用簡化版的欄位初始化方法 // 引數與結構體欄位擁有完全一致的名稱,所以可以使用名為欄位初始化簡寫的語法(field init shorthand) email, username, active: true, sign_in_count: 1, } }
使用結構體更新語法根據其他例項建立新例項
let user2 = User {
email: String::from("[email protected]"),
username: String::from("anotherusername455"),
..user1 // .. 表明剩下的那些還未被顯示賦值欄位都與給定例項擁有相同的值
};
使用不需要對欄位命名的元組結構體來建立不同的型別
元組結構體, 元祖結構體同樣擁有用於表明自身含義的名稱,無須在宣告它時對其欄位進行命名,僅保留欄位的型別即可。
一般來說,當你想要給元組賦予名字,並使其區別與其他擁有同樣定義的元組時,可以使用元祖結構體。
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let black = Color(0,0,0);
let origin = Point(0,0,0);
這裡的 black ,origin 是不同的型別,因為它們兩個分別是不同元組結構體的例項,
所以定義的每一個結構體都擁有自己的型別,即便結構體中的欄位擁有完全相同的型別。 例如,一個以Color型別作為引數的函式不能合法地接收Point型別的引數,即使它們都是由3個i32組成的。
可以通過模式匹配解構為單獨的部分,也可以通過.及索引來訪問特定欄位
沒有任何欄位的空結構體
單元結構體,沒有任何欄位的結構體()。也稱為空結構體
當需要在某些型別實現一個trait,卻不需要在該型別中儲存任何資料時,空結構體可以發揮作用。
//對於引用來說有生命週期的限制
struct User {
username: &str, // expected lifetime parameter
email: &str,
sign_in_count: u64,
active: bool,
}
fn main() {
let user1 = User {
email: "[email protected]",
username: "someusername123",
active: true,
aign_in_count: 1,
};
}
一個結構體的示例程式
fn main() {
let width1 = 30;
let height1 = 50;
println!("The area of the rectangel is {} aquare pixels.", area(width1, height1));
}
fn area(width: u32, height: u32) -> u32 {
width * height
}
元組重構
fn main() {
let rect1 = (30, 50);
println!("The area of the rectangel is {} aquare pixels.", area(rect1));
}
fn area(dimensions: (u32, u32)) -> u32 {
dimensions.0 * dimensions.1
}
使用結構體來重構程式碼,增加有意義的描述資訊
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!("The area of the rectangel is {} aquare pixels.", area(&rect1));
}
fn area(rectange: &Rectangle) -> u32 { //不可變借用&Rectangle ,不會獲得它的所有權
rectangle.width * rectangle.height
}
通過派生trait增加適用功能
#[derive(Debug)]
struct Rectange {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };;
println!("rect1 is {:?}", rect1);
}
方法
方法總是被定義在某個結構體、列舉體或者triat物件,並且它們的第一個引數永遠都是self,用於指代呼叫該方法的結構體例項。
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 { // self: &Self ---> Self is Rectangle
self.width * sellf.height
}
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!("The area of the rectangel is {} aquare pixels.", rect1.area());
}
為了在Rectangle的上下文環境中定義這個函式,需要將area函式移動到impl 關鍵字起始的程式碼塊中,並把簽名中的第一個引數(也是唯一的那個引數)和函式中使用該引數的地方改為self.
方法呼叫是通過例項後面加點好,並更上方法名、括號及可能的引數來實現。
帶有更多引數的方法
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
let rect2 = Rectangle { width: 10, height: 40 };
let rect3 = Rectangel { width: 60, height: 45 };
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 { // self: &Self ---> Self is Rectangle
self.width * sellf.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
關聯函式
impl Rectangle {
fn squaer(size: u32) -> Rectange {
Rectange { width: size, height: size }
}
}
關聯函式常常用作構造器來返回一個結構體的新例項。
型別名稱後面新增::來呼叫關聯函式
:: 語法不僅被用於關聯函式,還被用於模組建立的明名稱空間。
多個imple塊
impl Rectangle {
fn area(&self) -> u32 { // self: &Self ---> Self is Rectangle
self.width * sellf.height
}
}
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}