Rust 结构体

在 Rust 中,结构体(structure)是一种自定义数据类型,允许您将相关的值组合成一个有意义的单元。本教程将深入介绍 Rust 结构体,包括如何创建、使用和操作它们。

创建结构体

要定义一个结构体,请使用 struct 关键字并为整个结构体提供一个名称。结构体的名称应该描述其所包含的数据的意义。例如:

struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

在这个示例中,我们定义了一个名为 User 的结构体,它包含四个字段:usernameemailsign_in_countactive。每个字段都有其自己的类型,这些类型指定该字段可以保存的数据种类。

创建结构体实例

要使用结构体,首先需要创建一个结构体的实例。你可以通过在变量名称后跟随花括号 {} 中的键值对列表来做到这一点。例如:

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

请注意,字段的顺序不必与结构体定义中声明的顺序相同。但是,对于较大的结构体,通常更容易使用键值对语法来保持代码可读性。

访问结构体字段

要访问结构体实例的字段,请使用点号 . 后跟字段名称。例如:

println!("{}", user1.email); // 打印 "someone@example.com"

你还可以通过变量名来改变结构体实例的某个字段的值,但前提是该结构体实例是可变的。这意味着它需要使用 mut 关键字标记为可变。例如:

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

user1.email = String::from("anotheremail@example.com");

结构体更新语法

Rust 提供了一种更新结构体实例特定字段的快捷方式,称为“结构体更新语法”。该语法使用与创建结构体实例时相同的语法,但可以指定仅需要更改的字段,而不是重写整个结构体实例。这在只需要修改结构体中某些值而保持其他值不变时非常方便。

下面是一个使用结构体更新语法创建新用户的示例:

let user2 = User {
    email: String::from("another@example.com"),
    username: String::from("anotherusername567"),
    ..user1
};

在这个例子中,我们创建了一个新的 User 实例 user2。我们为 emailusername 字段提供了新值,但对于其他字段,我们使用了结构体更新语法(..user1)来从 user1 复制剩余的值。

元组结构体

Rust 还支持一种称为“元组结构体”的特殊类型的结构体,其行为类似于元组(tuple),但具有结构体名称提供的附加意义。元组结构体没有命名字段,只有相关联的数据类型。

要定义元组结构体,请在结构体名称后跟随一个包含类型的元组。例如:

struct Color(u8, u8, u8);

这个 Color 结构体只有一个元组作为其字段,而不是具有多个命名字段的常规结构体。元组结构体看起来像普通的函数调用。创建元组结构体实例时,需要提供所有字段的值:

let black = Color(0, 0, 0);

单元结构体

Rust 还支持没有任何字段的特殊类型的结构体,称为“单元结构体”。这些通常用于需要实现某个特征(trait)但不需要在该类型中存储数据的情况下。

要定义单元结构体,请使用 struct 关键字后跟分号:

struct Unit;

这里定义了一个名为 Unit 的单元结构体。我们不需要在大括号内添加任何东西,因为它没有任何数据要存储。

创建单元结构体实例时,只需使用结构体名称后跟随一对空括号:

let unit = Unit;

结构体作为函数参数

将结构体作为函数参数是非常常见的。这允许你将多个相关值封装成一个单独的实体,并且可以在整个程序中轻松地传递它们。

下面是一个示例,演示如何将结构体作为函数参数使用:

fn print_user(user: &User) {
    println!("{} - {}", user.username, user.email);
}

let user = User {
    username: String::from("exampleuser"),
    email: String::from("example@example.com"),
    sign_in_count: 1,
    active: true,
};

print_user(&user);

在这个示例中,我们定义了一个名为 print_user 的函数,它接受一个对 User 结构体实例的引用作为参数。然后我们创建一个新的 User 实例并将其传递给 print_user 函数,该函数打印出用户的用户名和电子邮件地址。

结构体和所有权

在 Rust 中,当你将结构体作为函数参数传递时,所有权规则适用于整个结构体。这意味着如果你传递结构体的所有权(例如,通过值而不是引用)给函数,该结构体将在函数调用后被移动并且无法再在当前作用域中使用。

下面是一个示例,演示了这一点:

fn take_user(user: User) {
    println!("{} has been taken!", user.username);
}

let user = User {
    username: String::from("takenuser"),
    email: String::from("taken@example.com"),
    sign_in_count: 1,
    active: true,
};

take_user(user);
// println!("{}", user.username); // 这将导致编译错误,因为 `user` 已经被移动了

在此示例中,我们定义了一个名为 take_user 的函数,它接受一个 User 结构体实例作为参数。然后我们创建一个新的 User 实例并将其传递给 take_user 函数。由于 take_user 函数取得了 user 的所有权,因此我们不再能够在 println! 语句后使用 user.username,这将导致编译错误。

结论

Rust 结构体是一种强大的工具,可以帮助您组织和操作相关数据。本教程涵盖了结构体的基础知识,包括如何创建、使用和操作它们,以及将结构体与函数参数一起使用时需要注意的所有权规则。希望你现在对 Rust 结构体更加熟悉!