定義和實例化結(jié)構(gòu)體和tuple類似,都可以將一些相關(guān)的值組織在一起,唯一不同的是結(jié)構(gòu)體里面的每一個變量需要命名,所以可讀性上比tuple更好,而且也不用關(guān)心里面的值的順序了。定義和賦值跟golang很像,直接用例子來看: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| struct User { active: bool, username: String, email: String, sign_in_count: u64, }
fn main() { let user1 = User { email: String::from("someone@example.com"), username: String::from("someusername123"), active: true, sign_in_count: 1, }; }
|
然后學(xué)一種struct賦值的簡易形式,先看基本的: 1 2 3 4 5 6 7 8
| fn build_user(email: String, username: String) -> User { User { email: email, username: username, active: true, sign_in_count: 1, } }
|
email和username的參數(shù)名字和struct中的field的名字一致,那就可以簡寫: 1 2 3 4 5 6 7 8
| fn build_user(email: String, username: String) -> User { User { email, username, active: true, sign_in_count: 1, } }
|
用一個struct給另一個struct賦值還是以上面那個struct為例,假設(shè)有一個user2,它的field中username、active、sign_in_count的值和user1一樣,只有email不一樣,那一般是這樣賦值: 1 2 3 4 5 6 7 8 9 10
| fn main() { // --snip--
let user2 = User { active: user1.active, username: user1.username, email: String::from("another@example.com"), sign_in_count: user1.sign_in_count, }; }
|
不過Rust中有更方便的方式: 1 2 3 4 5 6 7 8
| fn main() { // --snip--
let user2 = User { email: String::from("another@example.com"), ..user1 }; }
|
這寫法有點像ES6的語法,只不過ES6中是三個. ,而Rust是一個. 。不過需要注意,如果user1中有field發(fā)生“move”行為,那user1中那個field就失效了。就像上面這個例子,user1中的username,在賦值user2后,就失效了,后面不可以再訪問。
tuple struct結(jié)構(gòu)體Rust允許定義類似tuple的struct,像這樣: 1 2 3 4 5 6 7
| struct Color(i32, i32, i32); struct Point(i32, i32, i32);
fn main() { let black = Color(0, 0, 0); let origin = Point(0, 0, 0); }
|
這里的black和orgin雖然看似值一樣,但是不能互相賦值,因為類型不同。
空結(jié)構(gòu)體就是沒有任何字段的結(jié)構(gòu)體: 1 2 3 4 5
| struct AlwaysEqual;
fn main() { let subject = AlwaysEqual; }
|
關(guān)于struct的ownership在struct中,一般不使用引用,因為Rust希望struct中的字段都能完整的擁有值,有統(tǒng)一的生命周期。如果要在其中使用引用,必須申明“生命周期”(lifetime),這個概念我們后面看。先看這個錯誤的例子: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| struct User { active: bool, username: &str, email: &str, sign_in_count: u64, }
fn main() { let user1 = User { email: "someone@example.com", username: "someusername123", active: true, sign_in_count: 1, }; }
|
將會報錯: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| $ cargo run Compiling structs v0.1.0 (file:///projects/structs) error[E0106]: missing lifetime specifier --> src/main.rs:3:15 | 3 | username: &str, | ^ expected named lifetime parameter | help: consider introducing a named lifetime parameter | 1 ~ struct User<'a> { 2 | active: bool, 3 ~ username: &'a str, |
error[E0106]: missing lifetime specifier --> src/main.rs:4:12 | 4 | email: &str, | ^ expected named lifetime parameter | help: consider introducing a named lifetime parameter | 1 ~ struct User<'a> { 2 | active: bool, 3 | username: &str, 4 ~ email: &'a str, |
|
struct打印struct默認(rèn)情況下不能直接用println進(jìn)行打印,看下面的例子: 1 2 3 4 5 6 7 8 9 10 11 12 13
| struct Rectangle { width: u32, height: u32, }
fn main() { let rect1 = Rectangle { width: 30, height: 50, };
println!("rect1 is {}", rect1); }
|
這將會報錯: 1
| error[E0277]: `Rectangle` doesn't implement `std::fmt::Display`
|
需要打開debug,才可以使用{:?} 打印,或者{:#?} 以更好看的形式打?。?/p>1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #[derive(Debug)] struct Rectangle { width: u32, height: u32, }
fn main() { let rect1 = Rectangle { width: 30, height: 50, };
println!("rect1 is {:#?}", rect1); }
|
打印效果是這樣的: 1 2 3 4 5 6 7 8
| $ cargo run Compiling rectangles v0.1.0 (file:///projects/rectangles) Finished dev [unoptimized + debuginfo] target(s) in 0.48s Running `target/debug/rectangles` rect1 is Rectangle { width: 30, height: 50, }
|
標(biāo)準(zhǔn)錯誤輸出dbg!是標(biāo)準(zhǔn)錯誤輸出的宏,跟標(biāo)準(zhǔn)輸出println對應(yīng): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #[derive(Debug)] struct Rectangle { width: u32, height: u32, }
fn main() { let scale = 2; let rect1 = Rectangle { width: dbg!(30 * scale), height: 50, };
dbg!(&rect1); }
|
可以看到如下輸出: 1 2 3 4 5 6 7 8 9
| $ cargo run Compiling rectangles v0.1.0 (file:///projects/rectangles) Finished dev [unoptimized + debuginfo] target(s) in 0.61s Running `target/debug/rectangles` [src/main.rs:10] 30 * scale = 60 [src/main.rs:14] &rect1 = Rectangle { width: 60, height: 50, }
|
struct的方法定義實現(xiàn)一個struct的方法,這個有點類似go語言,看下面的例子: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #[derive(Debug)] 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!( "The area of the rectangle is {} square pixels.", rect1.area() ); }
|
關(guān)鍵字impl 表示實現(xiàn)后面的struct的方法,在里面所定義的所有方法,都從屬于這個struct。
area方法第一個參數(shù)是&self ,表示這個struct的實例的引用,而在使用中,我們用rect1 這個實例,調(diào)用area方法,這個實例就是代表了&self ,所以area就不用這個參數(shù)了傳入了。
在C/C++中,其實還有一個-> 符號調(diào)用方法的語法,當(dāng)對象實例調(diào)用內(nèi)部方法,那就是用. ,當(dāng)對象指針調(diào)用內(nèi)部方法,就是用-> ,不過Rust沒有這么麻煩,它會自行判斷,幫你添加. ,& 或者* ,來匹配方法調(diào)用。所以下面兩種寫法其實是一回事,沒有區(qū)別: 1 2
| p1.distance(&p2); (&p1).distance(&p2);
|
關(guān)聯(lián)函數(shù)關(guān)聯(lián)函數(shù)(Associated functions),其實就是java中的靜態(tài)函數(shù)的概念,在Rust中這樣定義: 1 2 3 4 5 6 7 8
| impl Rectangle { fn square(size: u32) -> Rectangle { Rectangle { width: size, height: size, } } }
|
這個在impl中的方法,沒有把&self 作為參數(shù),這個方法稱為關(guān)聯(lián)函數(shù),它的調(diào)用方法是使用:: ,看下面的例子:
1
| let sq = Rectangle::square(3);
|
Rust學(xué)習(xí)筆記(1)
Rust學(xué)習(xí)筆記(2)- Cargo包管理器
Rust學(xué)習(xí)筆記(3)- 變量和可變屬性
Rust學(xué)習(xí)筆記(4)-Ownership
|