Skip to content

Rust - 结构体

虽然数组和向量非常适合存储相同类型的列表,但大多数真实世界的数据更为复杂。结构体(structure),或简称 struct,是一种自定义数据类型,允许你将构成有意义实体的多个相关值组合并命名。这些值,称为字段(fields),可以是不同的类型。

结构体是为应用程序域建模的基本构建块。例如,你可以创建一个 User 结构体来将用户名、电子邮件和活动状态都存储在一个地方。

你使用 struct 关键字定义结构体,后跟其名称以及大括号内的字段及其类型。要使用结构体,你通过为每个字段指定具体值来创建其实例(instance)。

// Add this attribute to allow printing the struct for debugging.
#[derive(Debug)]
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn main() {
// Create an instance of the User struct.
let user1 = User {
email: String::from("contact@example.com"),
username: String::from("exampleuser123"),
active: true,
sign_in_count: 1,
};
// Access fields using dot notation.
println!("Username: {}", user1.username);
// Print the entire struct using the debug formatter.
println!("User details: {:?}", user1);
}
Username: exampleuser123
User details: User { username: "exampleuser123", email: "contact@example.com", sign_in_count: 1, active: true }

要修改结构体的字段,整个实例必须用 mut 关键字标记为可变。Rust 不允许将单个字段标记为可变。

#[derive(Debug)]
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn main() {
let mut user1 = User {
email: String::from("contact@example.com"),
username: String::from("exampleuser123"),
active: true,
sign_in_count: 1,
};
println!("Original email: {}", user1.email);
// Modify a field on the mutable instance.
user1.email = String::from("new.email@example.com");
println!("New email: {}", user1.email);
}
Original email: contact@example.com
New email: new.email@example.com

理解结构体如何与函数交互是理解 Rust 所有权系统(ownership system)的关键。你可以通过值(移动所有权)或通过引用(借用)将结构体传递给函数。

如果你直接传递一个结构体,所有权就会被移动到函数中。原始变量将不能再被使用。

struct Article { title: String, author: String }
// This function takes ownership of the Article.
fn process_article(article: Article) {
println!("Processing article: '{}' by {}", article.title, article.author);
}
fn main() {
let my_article = Article {
title: String::from("Understanding Ownership"),
author: String::from("Rust Coder")
};
process_article(my_article); // Ownership is moved here.
// The following line would cause a compile error because `my_article` has been moved.
// println!("Title: {}", my_article.title);
}

要允许函数使用结构体而不获取所有权,请传递它的引用(&)。对于只读访问,这几乎总是首选。

struct Article { title: String, author: String }
// This function borrows the Article immutably.
fn display_article(article: &Article) {
println!("Displaying article: '{}' by {}", article.title, article.author);
}
fn main() {
let my_article = Article {
title: String::from("Understanding Borrows"),
author: String::from("Rust Coder")
};
display_article(&my_article); // We pass a reference.
// This is fine! `my_article` was only borrowed, not moved.
println!("The article is still available: {}", my_article.title);
}

方法是定义在结构体(或枚举、特性)上下文中的函数。它们使用 impl(实现)块声明。方法的第一个参数总是 self,它代表调用该方法的结构体实例。

#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
// Implementation block for the Rectangle struct.
impl Rectangle {
// This is a method. `&self` is a shorthand for `self: &Self`.
// It borrows the instance immutably.
fn area(&self) -> u32 {
self.width * self.height
}
// Another method to check if a rectangle can hold another.
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
let rect2 = Rectangle { width: 10, height: 40 };
println!(
"The area of the rectangle is {} square pixels.",
rect1.area() // Call the method using dot notation.
);
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
}
The area of the rectangle is 1500 square pixels.
Can rect1 hold rect2? true

在 impl 块中不将 self 作为参数的函数称为关联函数(associated functions)。它们与结构体类型本身相关联,而不是与特定实例关联。它们通常用作构造函数来创建结构体的新实例。构造函数的惯用名称是 new。

struct Point { x: f64, y: f64 }
impl Point {
// An associated function, often used as a constructor.
// It's called using the `::` syntax.
fn new(x: f64, y: f64) -> Point {
Point { x, y } // Field init shorthand syntax
}
// Another associated function.
fn origin() -> Point {
Point { x: 0.0, y: 0.0 }
}
// A method to display the point.
fn display(&self) {
println!("Point(x: {}, y: {})", self.x, self.y);
}
}
fn main() {
// Call the `new` associated function.
let p1 = Point::new(10.5, 20.2);
p1.display();
// Call the `origin` associated function.
let origin = Point::origin();
origin.display();
}
Point(x: 10.5, y: 20.2)
Point(x: 0.0, y: 0.0)

Rust 还提供了另外两种结构体变体,适用于不同的用例:

  • 元组结构体(Tuple Structs):当你想要给一个元组命名并使其成为一个独特的类型,但字段不需要名称时非常有用。例如:struct Color(u8, u8, u8);
  • 单元结构体(Unit-Like Structs):没有任何字段的结构体。它们在泛型编程或当你需要为一个类型实现一个特性(trait)但没有数据要存储时非常有用。例如:struct AlwaysEqual;