diff --git a/en/src/SUMMARY.md b/en/src/SUMMARY.md index 854051f..f27ac0a 100644 --- a/en/src/SUMMARY.md +++ b/en/src/SUMMARY.md @@ -15,7 +15,7 @@ - [Array](compound-types/array.md) - [Slice](compound-types/slice.md) - [Tuple](compound-types/tuple.md) - - [Struct todo](compound-types/struct.md) + - [Struct](compound-types/struct.md) - [Enum todo](compound-types/enum.md) - [Flow Control todo](flow-control.md) - [Pattern Match todo](pattern-match/intro.md) diff --git a/en/src/compound-types/struct.md b/en/src/compound-types/struct.md index 4b9489b..58c8468 100644 --- a/en/src/compound-types/struct.md +++ b/en/src/compound-types/struct.md @@ -1 +1,216 @@ -# struct +# Struct + +### There types of structs +🌟 We must specify concrete values for each of the fields in struct. +```rust,editable + +// fix the error +struct Person { + name: String, + age: u8, + hobby: String +} +fn main() { + let age = 30; + let p = Person { + name: String::from("sunface"), + age, + }; +} +``` + + +🌟 Unit struct don't have any fields. It can be useful when you need to implement a trait on some type but don’t have any data that you want to store in the type itself. +```rust,editable + +struct Unit; +trait SomeTrait { + // ...Some behavours defines here +} + +// We don't care the the fields are in Unit, but we care its behaviors. +// So we use a struct with no fields and implement some behaviors for it +impl SomeTrait for Unit { } +fn main() { + let u = Unit; + do_something_with_unit(u); +} + +// fill the blank to make the code work +fn do_something_with_unit(u: __) { } +``` + +🌟🌟🌟 Tuple struct looks similar to tuples, it has added meaning the struct name provides but has no named fields. It's useful when you want give the whole tuple a name, but don't care the fields's names. + +```rust,editable + +// fix the error and fill the blanks +struct Color(i32, i32, i32); +struct Point(i32, i32, i32); +fn main() { + let v = Point(__, __, __); + check_color(v); +} + +fn check_color(p: Color) { + let (x, _, _) = p; + assert_eq!(x, 0); + assert_eq!(p.1, 127); + assert_eq!(__, 255); + } +``` + +### Operate on structs + +🌟 You can make a whole struct mutable when instantiate it, but Rust doesn't allow us to mark only certain fields as mutable. + +```rust,editable + +// fill the blank and fix the error without adding/removing new line +struct Person { + name: String, + age: u8, +} +fn main() { + let age = 18; + let p = Person { + name: String::from("sunface"), + age, + }; + + // how can you believe sunface is only 18? + p.age = 30 + + // fill the lank + __ = String::from("sunfei"); +} +``` + +🌟 Using *field init shorthand syntax* to reduct repetitions. +```rust,editable + +// fill the blank +struct Person { + name: String, + age: u8, +} +fn main() {} + +fn build_person(name: String, age: u8) -> Person { + Person { + age, + __ + } +} +``` + +🌟 You can create instance from other instance with *struct update syntax* +```rust,editable + +// fill the blank to make the code work +struct User { + active: bool, + username: String, + email: String, + sign_in_count: u64, +} +fn main() { + let u1 = User { + email: String::from("someone@example.com"), + username: String::from("sunface"), + active: true, + sign_in_count: 1, + }; + + let u2 = set_email(u1); +} + +fn set_email(u: User) -> User { + User { + email: String::from("contact@im.dev"), + __ + } +} +``` + +### Print the structs +🌟🌟 We can use `#[derive(Debug)]` to [make a struct prinable](https://doc.rust-lang.org/book/ch05-02-example-structs.html?highlight=%23%5Bderive(Debug)%5D#adding-useful-functionality-with-derived-traits). + +```rust,editable + +// fill the blanks to make the code work +#[__] +struct Rectangle { + width: u32, + height: u32, +} + +fn main() { + let scale = 2; + let rect1 = Rectangle { + width: dbg!(30 * scale), // print debug info to stderr and assign the value of `30 * scale` to `width` + height: 50, + }; + + dbg!(&rect1); // print debug info to stderr + + println!(__, rect1); // print debug info to stdout +} +``` + +### Partial move +Within the destructuring of a single variable, both by-move and by-reference pattern bindings can be used at the same time. Doing this will result in a partial move of the variable, which means that parts of the variable will be moved while other parts stay. In such a case, the parent variable cannot be used afterwards as a whole, however the parts that are only referenced (and not moved) can still be used. + +#### Example +```rust,editable + +fn main() { + #[derive(Debug)] + struct Person { + name: String, + age: Box, + } + + let person = Person { + name: String::from("Alice"), + age: Box::new(20), + }; + + // `name` is moved out of person, but `age` is referenced + let Person { name, ref age } = person; + + println!("The person's age is {}", age); + + println!("The person's name is {}", name); + + // Error! borrow of partially moved value: `person` partial move occurs + //println!("The person struct is {:?}", person); + + // `person` cannot be used but `person.age` can be used as it is not moved + println!("The person's age from person struct is {}", person.age); +} +``` + + +#### Exercises + +🌟🌟 +```rust,editable + +// fix errors to make it work +#[derive(Debug)] +struct File { + name: String, + data: String, +} +fn main() { + let f = File { + name: String::from("readme.md"), + data: "Rust By Practice".to_string() + }; + + let _name = f.name; + + println!("{}, {}, {:?}",f.name, f.data, f); +} +``` \ No newline at end of file diff --git a/zh-CN/src/SUMMARY.md b/zh-CN/src/SUMMARY.md index 661359a..0cd97e8 100644 --- a/zh-CN/src/SUMMARY.md +++ b/zh-CN/src/SUMMARY.md @@ -15,7 +15,7 @@ - [数组](compound-types/array.md) - [切片](compound-types/slice.md) - [元组](compound-types/tuple.md) - - [结构体 undo](compound-types/struct.md) + - [结构体](compound-types/struct.md) - [枚举 undo](compound-types/enum.md) - [流程控制 todo](flow-control.md) - [模式匹配 todo](pattern-match/intro.md) diff --git a/zh-CN/src/compound-types/struct.md b/zh-CN/src/compound-types/struct.md index 4b9489b..b31a5f2 100644 --- a/zh-CN/src/compound-types/struct.md +++ b/zh-CN/src/compound-types/struct.md @@ -1 +1,218 @@ -# struct +# 结构体 + +### 三种类型的结构体 +🌟 对于结构体,我们必须为其中的每一个字段都指定具体的值 +```rust,editable + +// fix the error +struct Person { + name: String, + age: u8, + hobby: String +} +fn main() { + let age = 30; + let p = Person { + name: String::from("sunface"), + age, + }; +} +``` + + +🌟 单元结构体没有任何字段。 +```rust,editable + +struct Unit; +trait SomeTrait { + // ...定义一些行为 +} + +// 我们并不关心结构体中有什么数据( 字段 ),但我们关心它的行为。 +// 因此这里我们使用没有任何字段的单元结构体,然后为它实现一些行为 +impl SomeTrait for Unit { } +fn main() { + let u = Unit; + do_something_with_unit(u); +} + +// 填空,让代码工作 +fn do_something_with_unit(u: __) { } +``` + +🌟🌟🌟 元组结构体看起来跟元组很像,但是它拥有一个结构体的名称,该名称可以赋予它一定的意义。由于它并不关心内部数据到底是什么名称,因此此时元组结构体就非常适合。 + +```rust,editable + +// 填空并修复错误 +struct Color(i32, i32, i32); +struct Point(i32, i32, i32); +fn main() { + let v = Point(__, __, __); + check_color(v); +} + +fn check_color(p: Color) { + let (x, _, _) = p; + assert_eq!(x, 0); + assert_eq!(p.1, 127); + assert_eq!(__, 255); + } +``` + +### 结构体上的一些操作 +🌟 你可以在实例化一个结构体时将它整体标记为可变的,但是 Rust 不允许我们将结构体的某个字段专门指定为可变的. + +```rust,editable + +// 填空并修复错误,不要增加或移除代码行 +struct Person { + name: String, + age: u8, +} +fn main() { + let age = 18; + let p = Person { + name: String::from("sunface"), + age, + }; + + // how can you believe sunface is only 18? + p.age = 30 + + // 填空 + __ = String::from("sunfei"); +} +``` + +🌟 使用结构体字段初始化缩略语法可以减少一些重复代码 +```rust,editable + +// 填空 +struct Person { + name: String, + age: u8, +} +fn main() {} + +fn build_person(name: String, age: u8) -> Person { + Person { + age, + __ + } +} +``` + +🌟 你可以使用结构体更新语法基于一个结构体实例来构造另一个 +```rust,editable + +// 填空,让代码工作 +struct User { + active: bool, + username: String, + email: String, + sign_in_count: u64, +} +fn main() { + let u1 = User { + email: String::from("someone@example.com"), + username: String::from("sunface"), + active: true, + sign_in_count: 1, + }; + + let u2 = set_email(u1); +} + +fn set_email(u: User) -> User { + User { + email: String::from("contact@im.dev"), + __ + } +} +``` + +### 打印结构体 +🌟🌟 我们可以使用 `#[derive(Debug)]` 让[结构体变成可打印的](https://course.rs/basic/compound-type/struct.html#使用-derivedebug-来打印结构体的信息). + +```rust,editable + +// 填空,让代码工作 +#[__] +struct Rectangle { + width: u32, + height: u32, +} + +fn main() { + let scale = 2; + let rect1 = Rectangle { + width: dbg!(30 * scale), // 打印 debug 信息到标准错误输出 stderr,并将 `30 * scale` 的值赋给 `width` + height: 50, + }; + + dbg!(&rect1); // 打印 debug 信息到标准错误输出 stderr + + println!(__, rect1); // 打印 debug 信息到标准输出 stdout +} +``` + +### 结构体的所有权 +当解构一个变量时,可以同时使用 `move` 和引用模式绑定的方式。当这么做时,部分 `move` 就会发生:变量中一部分的所有权被转移给其它变量,而另一部分我们获取了它的引用。 + +在这种情况下,原变量将无法再被使用,但是它没有转移所有权的那一部分依然可以使用,也就是之前被引用的那部分。 + +#### 示例 +```rust,editable + +fn main() { + #[derive(Debug)] + struct Person { + name: String, + age: Box, + } + + let person = Person { + name: String::from("Alice"), + age: Box::new(20), + }; + + // 通过这种解构式模式匹配,person.name 的所有权被转移给新的变量 `name` + // 但是,这里 `age` 变量确是对 person.age 的引用, 这里 ref 的使用相当于: let age = &person.age + let Person { name, ref age } = person; + + println!("The person's age is {}", age); + + println!("The person's name is {}", name); + + // Error! 原因是 person 的一部分已经被转移了所有权,因此我们无法再使用它 + //println!("The person struct is {:?}", person); + + // 虽然 `person` 作为一个整体无法再被使用,但是 `person.age` 依然可以使用 + println!("The person's age from person struct is {}", person.age); +} +``` + +#### 练习 + +🌟🌟 +```rust,editable + +// 修复错误 +#[derive(Debug)] +struct File { + name: String, + data: String, +} +fn main() { + let f = File { + name: String::from("readme.md"), + data: "Rust By Practice".to_string() + }; + + let _name = f.name; + + println!("{}, {}, {:?}",f.name, f.data, f); +} +``` +```