Rust 学习 06

archive time: 2022-05-14

终于开始讲 struct 了

Struct

struct 是自定义类型, 是积类型, 与之相对的是枚举 (enum), 和类型

简单理解, 积类型就是说要把所有字段一起出现才能示例化的类型, 而和类型就是字段里出现一个就行

struct User {
    username: String,
    email: String,
    acount: u16,
    active: bool,
}

// ...

let user = User { // 顺序可以不一样
    // 但是每个字段都需要赋值
    email: String::from("someone@example.com"),
    username: String::from("someuser"),
    active: true,
    acount: 1,
}; // 注意分号
// 访问某个字段
println!("{}", user.email); // => someone@example.com

如果要对某个字段重新赋值或者修改值, 那么可以将实例声明为可变的

但是如果实例是可变的, 那么每个字段都将是可变的

// 现在实例是可变的
let mut user = User {
    email: String::from("someone@example.com"),
    username: String::from("someuser"),
    active: true,
    acount: 1,
};
// ...
user.username = String::from("kandscode");
println!("{}", user.username); // => kandscode

如果用于实例的变量和字段名相同, 可以简写

let email = String::from("someone@example.com");

let user = User {
    email, // 简写
    username: String::from("someuser"),
    active: true,
    acount: 1,
};

我们还可以从一个现有的结构体实例来创建一个新的实例

let user1 = User {
    email: String::from("someone@example.com"),
    username: String::from("someuser"),
    active: true,
    acount: 1,
};

let user2 = User {
    acount: 1, // 除了 acount 不一样
    ..user1  // 其余和 user1 一样
};

但是要小心, 这样创建的实例之间是有关联的

let user1 = User {
    email: String::from("someone@example.com"),
    username: String::from("someuser"),
    active: true,
    acount: 1,
};

let mut user2 = User {
    acount: 1,
    ..user1
};

// 不允许 因为 String 没有实现 Copy trait
// 所以这里是移动语义
user2.email = String::from("someone@another.com");
println!("{}", user1.email);

对应报错是

error[E0382]: borrow of moved value: `user1.email`
  --> src\main.rs:20:20
   |
14 |       let mut user2 = User {
   |  _____________________-
15 | |         acount: 1,
16 | |         ..user1
17 | |     };
   | |_____- value moved here
...
20 |       println!("{}", user1.email);
   |                      ^^^^^^^^^^^ value borrowed here after move
   |
   = note: move occurs because `user1.email` has type `String`, which does not implement the `Copy` trait
   = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)

所以要注意类型, 小心使用这个语法

但是这样是可以的

println!("{}", user1.active); // user1 还是可用

Tuple-Like Struct

tuple-like struct 整体有名字, 但是字段没有名称

struct Color(i8, i8, i8);
// ...
let black = Color(0, 0, 0);
println!("{}", black.0) // 访问第一个字段

Unit-Like Struct

就是没有字段的结构体

一般用于想在某个类型上实现某个 trait 但是又没有数据

struct AlwaysEqual;
let subject = AlwaysEqual;
// 我们不关心 AlwaysEqual 的字段数据, 只关心它的行为
// 因此将它声明为单元结构体, 然后再为它实现某个特征
impl SomeTrait for AlwaysEqual {
    // ...
}

Example

一个计算矩形面积的例子

#[derive(Debug)] // 默认实现Debug
struct Rectangle {
    width: u8,
    length: u8,
}

fn area(rec: &Rectangle) -> u8 {
    return rec.width * rec.length;
}

fn main() {
    let rec = Rectangle {
        length: 8,
        width: 7,
    };
    println!("{}", area(&rec));
    // {:#?} 要比 {:?} 要更加清晰
    println!("{:#?}", rec); // 使用debug方式打印
}
// =>
/** output
56
Rectangle {
    width: 7,
    length: 8,
}
**/

好, 暂时就学到这里罢, 原神, 启动!