
结构体:数据的"收纳盒",把相关字段装一起
你有没有写过这样的代码:
let user_name = "Alice";
let user_age = ;
let user_email = "alice@example.com";
三个变量,都表示同一个用户的信息,但它们之间没有任何关联。如果你想把这个用户传给一个函数,得传三个参数。如果你想存到数组里?抱歉,不行。
这时候就需要**结构体(Struct)**了。结构体就像是一个"数据收纳盒",把相关的字段打包在一起。从此以后,一个 User 就代表一个用户,清爽!
今天咱们就来聊聊 Rust 的结构体,看看它怎么帮你组织数据,以及它和其他语言的"类"有什么不一样。
结构体(Struct)是自定义的数据类型,用来把多个相关字段组合在一起。
生活化类比:
struct 关键字struct User {
username: String,
email: String,
age: u32,
active: bool,
}
注意:
: 隔开let user1 = User {
username: String::from("alice"),
email: String::from("alice@example.com"),
age: ,
active: true,
};
访问字段:用点号 .
println!("用户名:{}", user1.username);
println!("年龄:{}", user1.age);
如果你想基于一个现有结构体创建新实例,只改几个字段,可以用更新语法:
let user2 = User {
username: String::from("bob"),
..user1 // 其他字段从 user1 复制
};
注意:..user1 必须放在最后!
// ✅ 正确
let user2 = User {
username: String::from("bob"),
..user1
};
// ❌ 错误
let user2 = User {
..user1
username: String::from("bob"), // 编译错误
};
有时候你只需要一个"有类型的元组",不需要字段名,这时候可以用元组结构体:
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let black = Color(, , );
let origin = Point(, , );
// 访问字段用索引
println!("R 分量:{}", black.);
println!("X 坐标:{}", origin.);
用途:给元组加"语义"。Color(0, 0, 0) 比 (0, 0, 0) 更清楚这是颜色而不是坐标。
struct Marker;
用途:
结构体不光能存数据,还能有方法(跟数据绑定的函数)。
impl User {
fn greet(&self) {
println!("你好,我是{}!", self.username);
}
fn have_birthday(&mut self) {
self.age += ;
}
}
调用方法:
let mut user = User {
username: String::from("alice"),
email: String::from("alice@example.com"),
age: ,
active: true,
};
user.greet(); // 你好,我是 alice!
user.have_birthday(); // age 变成 26
self 参数:方法的"主人"方法的第一参数通常是 self,有三种形式:
形式 | 含义 | 用途 |
|---|---|---|
&self | 不可变借用 | 只读方法 |
&mut self | 可变借用 | 修改数据 |
self | 获取所有权 | 消耗掉结构体 |
impl User {
// 只读
fn get_username(&self) -> &str {
&self.username
}
// 修改
fn set_username(&mut self, new_name: String) {
self.username = new_name;
}
// 消耗
fn into_username(self) -> String {
self.username // 结构体被"拆解"了
}
}
self 的"静态方法"关联函数是没有 self 参数的方法,通常用作构造函数:
impl User {
fn new(username: String, email: String) -> User {
User {
username,
email,
age: ,
active: true,
}
}
}
// 调用
let user = User::new(
String::from("alice"),
String::from("alice@example.com"),
);
注意:username, email 是字段初始化简写——当参数名和字段名相同时,可以省略 username: username。
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// 关联函数(构造函数)
fn new(width: u32, height: u32) -> Self {
Rectangle { width, height }
}
// 方法:计算面积
fn area(&self) -> u32 {
self.width * self.height
}
// 方法:能否装下另一个矩形
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
fn main() {
let rect1 = Rectangle::new(, );
let rect2 = Rectangle::new(, );
println!("面积:{}", rect1.area()); // 1500
println!("能装下 rect2 吗?{}", rect1.can_hold(&rect2)); // true
}
struct User {
username: String,
age: u32,
}
fn main() {
let user = User {
age: ,
username: String::from("alice"), // 顺序不同
};
}
编译器错误:
error[E0560]: struct `User` has no field named `username`
--> src/main.rs:8:9
|
8 | username: String::from("alice"),
| ^^^^^^^^ help: a field with a similar name exists: `age`
人话翻译:等等...这个错误不对。实际上字段顺序可以不同。让我换个真正的错误。
struct User {
username: String,
age: u32,
}
fn main() {
let user = User {
username: String::from("alice"),
// 忘记 age 字段
};
}
编译器错误:
error[E0063]: missing field `age` in initializer of `User`
--> src/main.rs:7:16
|
7 | let user = User {
| ^^^^ missing `age`
人话翻译:编译器:"你说要创建 User,但 age 字段呢?别想蒙混过关!"
struct User {
username: String,
age: u32,
}
fn main() {
let user = User {
username: String::from("alice"),
age: ,
};
user.age = ; // 编译错误!
}
编译器错误:
error[E0594]: cannot assign to `user.age`, as `user` is not declared as mutable
--> src/main.rs:12:5
|
12 | user.age = 26;
| ^^^^^^^^^^^^^^ cannot assign
|
help: consider changing this to be mutable
|
5 | let mut user = User {
| +++
人话翻译:编译器:"user 不是可变的,不能改!加个 mut 啊!"
struct User {
username: String,
}
impl User {
fn greet(self) { // 用了 self,不是 &self
println!("Hello, {}", self.username);
}
}
fn main() {
let user = User {
username: String::from("alice"),
};
user.greet();
user.greet(); // 编译错误:user 已经被移动了
}
编译器错误:
error[E0382]: borrow of moved value: `user`
--> src/main.rs:17:5
|
12 | let user = User { ... };
| ---- move occurs because `user` has type `User`,
| which does not implement the `Copy` trait
13 | user.greet();
| ----------- `user` moved due to this method call
...
17 | user.greet();
| ^^^^ value borrowed here after move
人话翻译:编译器:"greet 方法拿走了 user 的所有权,第一次调用后 user 就没了。第二次调用?不存在的!"
修复:用 &self。
fn greet(&self) {
println!("Hello, {}", self.username);
}
mutlet user = User { ... };
user.age = ; // 错误!
修复:let mut user = User { ... };
struct User {
name: String,
}
fn print_name(user: User) {
println!("{}", user.name);
}
fn main() {
let user = User { name: String::from("Alice") };
print_name(user);
print_name(user); // 错误:user 已移动
}
修复:用借用。
fn print_name(user: &User) {
println!("{}", user.name);
}
let user2 = User {
..user1
username: String::from("bob"), // 错误!
};
修复:..user1 必须放最后。
let user2 = User {
username: String::from("bob"),
..user1
};
self 的三种形式你想... | 用这个 |
|---|---|
只读数据 | &self |
修改数据 | &mut self |
消耗/转换结构体 | self |
struct Student {
id: u32,
name: String,
grades: Vec<f32>,
}
impl Student {
fn new(id: u32, name: String) -> Self {
Student { id, name, grades: Vec::new() }
}
fn add_grade(&mut self, grade: f32) {
self.grades.push(grade);
}
fn average(&self) -> f32 {
if self.grades.is_empty() {
return 0.0;
}
let sum: f32 = self.grades.iter().sum();
sum / self.grades.len() as f32
}
fn is_passing(&self) -> bool {
self.average() >= 60.0
}
}
fn main() {
let mut student = Student::new(, String::from("Alice"));
student.add_grade(85.0);
student.add_grade(92.0);
student.add_grade(78.0);
println!("平均分:{:.2}", student.average());
println!("及格了吗?{}", if student.is_passing() { "✅" } else { "❌" });
}
#[derive(Debug)]// 自动实现 Debug trait
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: , y: };
println!("{:?}", p); // Point { x: 10, y: 20 }
println!("{:#?}", p); // 格式化输出
}
常用派生宏:
#[derive(Debug)] - 调试输出#[derive(Clone)] - 克隆#[derive(Copy, Clone)] - 复制语义#[derive(PartialEq, Eq)] - 比较#[derive(Default)] - 默认值struct Request {
url: String,
method: String,
headers: Vec<String>,
}
impl Request {
fn new(url: String) -> Self {
Request {
url,
method: String::from("GET"),
headers: Vec::new(),
}
}
fn method(mut self, method: String) -> Self {
self.method = method;
self
}
fn header(mut self, header: String) -> Self {
self.headers.push(header);
self
}
fn send(self) {
println!("发送请求:{} {}", self.method, self.url);
println!("Headers: {:?}", self.headers);
}
}
fn main() {
Request::new(String::from("https://api.example.com"))
.method(String::from("POST"))
.header(String::from("Content-Type: application/json"))
.header(String::from("Authorization: Bearer token"))
.send();
}
输出:
发送请求:POST https://api.example.com
Headers: ["Content-Type: application/json", "Authorization: Bearer token"]

..other 必须放最后,其他字段从 other 复制&self 只读,&mut self 修改,self 消耗self,常用作构造函数#[derive()] 自动实现常用 Trait,省事儿下篇预告:结构体只能表示"是什么",那"有多种可能"怎么办?下一篇聊聊枚举与模式匹配,让你的代码能处理各种情况!