mirror of
https://github.com/sunface/rust-by-practice.git
synced 2025-06-23 04:29:41 +00:00
add struct.md
This commit is contained in:
@ -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)
|
||||
|
@ -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<u8>,
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
```
|
@ -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)
|
||||
|
@ -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<u8>,
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
```
|
||||
```
|
||||
|
Reference in New Issue
Block a user