Rust 系统编程语言完全教程 / 第07章:结构体
第07章:结构体
7.1 定义与实例化
基本结构体
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn main() {
// 创建实例(必须为所有字段赋值)
let user1 = User {
email: String::from("[email protected]"),
username: String::from("someuser"),
active: true,
sign_in_count: 1,
};
println!("用户名: {}", user1.username);
println!("邮箱: {}", user1.email);
}
可变结构体
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn main() {
// 整个实例要么可变要么不可变,不能只标记某个字段为可变
let mut user = User {
email: String::from("[email protected]"),
username: String::from("user"),
active: true,
sign_in_count: 1,
};
user.email = String::from("[email protected]");
println!("新邮箱: {}", user.email);
}
注意: Rust 不允许只将结构体的某个字段标记为可变。可变性是整个绑定的属性。
结构体更新语法
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn main() {
let user1 = User {
email: String::from("[email protected]"),
username: String::from("user"),
active: true,
sign_in_count: 1,
};
// 使用 ..user1 语法创建新实例,其余字段从 user1 获取
let user2 = User {
email: String::from("[email protected]"),
..user1 // 其余字段来自 user1
};
// user1.username 已移动到 user2,user1 不再完整
// println!("{}", user1.username); // ❌ 已移动
println!("{}", user2.username); // ✅
println!("{}", user2.email); // ✅ (新的邮箱)
// 未移动的字段仍可使用
println!("user1.active: {}", user1.active); // ✅ bool 是 Copy
println!("user1.sign_in: {}", user1.sign_in_count); // ✅ u64 是 Copy
}
7.2 元组结构体(Tuple Struct)
没有字段名,只有类型:
struct Color(i32, i32, i32);
struct Point(f64, f64, f64);
fn main() {
let black = Color(0, 0, 0);
let origin = Point(0.0, 0.0, 0.0);
println!("黑色: ({}, {}, {})", black.0, black.1, black.2);
println!("原点: ({}, {}, {})", origin.0, origin.1, origin.2);
// 虽然内部结构相同,但 Color 和 Point 是不同类型
// let c: Color = origin; // ❌ 类型不匹配
}
单元结构体(Unit-Like Struct)
没有任何字段:
struct Marker;
// 用于实现 trait,不需要存储数据
impl Marker {
fn mark(&self) {
println!("标记处理完成");
}
}
fn main() {
let m = Marker;
m.mark();
}
7.3 方法(Methods)
定义方法
#[derive(Debug)]
struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
// 方法的第一个参数是 &self(不可变引用)
fn area(&self) -> f64 {
self.width * self.height
}
fn perimeter(&self) -> f64 {
2.0 * (self.width + self.height)
}
// 可变方法
fn scale(&mut self, factor: f64) {
self.width *= factor;
self.height *= factor;
}
// 消费 self(获取所有权)
fn into_square(self) -> Rectangle {
let side = self.width.max(self.height);
Rectangle {
width: side,
height: side,
}
}
}
fn main() {
let mut rect = Rectangle {
width: 10.0,
height: 5.0,
};
println!("面积: {}", rect.area());
println!("周长: {}", rect.perimeter());
rect.scale(2.0);
println!("缩放后: {:?}", rect);
let square = rect.into_square();
println!("正方形: {:?}", square);
// println!("{:?}", rect); // ❌ rect 已被消费
}
self 的三种形式
| 形式 | 含义 | 何时使用 |
|---|---|---|
&self | 不可变借用 | 只读访问(最常用) |
&mut self | 可变借用 | 需要修改 |
self | 获取所有权 | 转换或消耗 |
方法链式调用
#[derive(Debug)]
struct QueryBuilder {
table: String,
conditions: Vec<String>,
limit: Option<usize>,
order_by: Option<String>,
}
impl QueryBuilder {
fn new(table: &str) -> Self {
Self {
table: table.to_string(),
conditions: Vec::new(),
limit: None,
order_by: None,
}
}
// 返回 &mut self 实现链式调用
fn where_clause(mut self, condition: &str) -> Self {
self.conditions.push(condition.to_string());
self
}
fn limit(mut self, n: usize) -> Self {
self.limit = Some(n);
self
}
fn order_by(mut self, column: &str) -> Self {
self.order_by = Some(column.to_string());
self
}
fn build(&self) -> String {
let mut sql = format!("SELECT * FROM {}", self.table);
if !self.conditions.is_empty() {
sql.push_str(" WHERE ");
sql.push_str(&self.conditions.join(" AND "));
}
if let Some(ref order) = self.order_by {
sql.push_str(&format!(" ORDER BY {}", order));
}
if let Some(limit) = self.limit {
sql.push_str(&format!(" LIMIT {}", limit));
}
sql
}
}
fn main() {
let query = QueryBuilder::new("users")
.where_clause("age > 18")
.where_clause("active = true")
.order_by("created_at DESC")
.limit(10)
.build();
println!("SQL: {}", query);
// SQL: SELECT * FROM users WHERE age > 18 AND active = true ORDER BY created_at DESC LIMIT 10
}
7.4 关联函数(Associated Functions)
不以 self 作为第一个参数的函数称为关联函数(类似其他语言的"静态方法"):
#[derive(Debug)]
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl Circle {
// 关联函数:不接收 self,常用于构造
fn new(x: f64, y: f64, radius: f64) -> Self {
Self { x, y, radius }
}
fn at_origin(radius: f64) -> Self {
Self::new(0.0, 0.0, radius)
}
fn unit() -> Self {
Self::at_origin(1.0)
}
// 方法:接收 &self
fn area(&self) -> f64 {
std::f64::consts::PI * self.radius * self.radius
}
fn circumference(&self) -> f64 {
2.0 * std::f64::consts::PI * self.radius
}
fn contains_point(&self, px: f64, py: f64) -> bool {
let dx = self.x - px;
let dy = self.y - py;
dx * dx + dy * dy <= self.radius * self.radius
}
}
fn main() {
// 使用关联函数创建实例(使用 :: 语法)
let c1 = Circle::new(1.0, 2.0, 5.0);
let c2 = Circle::at_origin(3.0);
let c3 = Circle::unit();
println!("c1: {:?}, 面积: {:.2}", c1, c1.area());
println!("c2: {:?}, 周长: {:.2}", c2, c2.circumference());
println!("c3: {:?}, 包含(0.5,0.5): {}", c3, c3.contains_point(0.5, 0.5));
}
多个 impl 块
struct Config {
host: String,
port: u16,
debug: bool,
}
// 可以有多个 impl 块(通常用于分组不同功能)
impl Config {
fn new() -> Self {
Self {
host: String::from("localhost"),
port: 8080,
debug: false,
}
}
}
impl Config {
fn is_debug(&self) -> bool {
self.debug
}
fn address(&self) -> String {
format!("{}:{}", self.host, self.port)
}
}
fn main() {
let config = Config::new();
println!("地址: {}, debug: {}", config.address(), config.is_debug());
}
7.5 打印结构体
使用 Debug trait
// 需要 derive Debug 才能用 {:?} 打印
#[derive(Debug)]
struct Point {
x: f64,
y: f64,
}
fn main() {
let p = Point { x: 3.0, y: 4.0 };
// {:?} 紧凑格式
println!("point: {:?}", p); // Point { x: 3.0, y: 4.0 }
// {:#?} 美化格式(多行)
println!("point:\n{:#?}", p);
}
实现 Display trait
use std::fmt;
struct Color {
r: u8,
g: u8,
b: u8,
}
impl fmt::Display for Color {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "#{:02X}{:02X}{:02X}", self.r, self.g, self.b)
}
}
impl fmt::Debug for Color {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Color(r={}, g={}, b={})", self.r, self.g, self.b)
}
}
fn main() {
let red = Color { r: 255, g: 0, b: 0 };
println!("Display: {}", red); // #FF0000
println!("Debug: {:?}", red); // Color(r=255, g=0, b=0)
}
7.6 常用 derive 宏
| derive | 功能 | 说明 |
|---|---|---|
Debug | 调试打印 | {:?} 格式 |
Clone | 深拷贝 | .clone() 方法 |
Copy | 位拷贝 | 赋值时自动复制 |
PartialEq | 部分相等 | == 和 != |
Eq | 完全相等 | PartialEq 的完全版 |
PartialOrd | 部分排序 | <, >, <=, >= |
Ord | 完全排序 | sort() 所需 |
Hash | 哈希 | 可用作 HashMap 键 |
Default | 默认值 | T::default() |
#[derive(Debug, Clone, PartialEq, Default)]
struct Student {
name: String,
age: u32,
grade: String,
}
fn main() {
// Default: 创建默认实例
let default_student = Student::default();
println!("默认学生: {:?}", default_student);
// Clone: 深拷贝
let s1 = Student {
name: "张三".to_string(),
age: 20,
grade: "A".to_string(),
};
let s2 = s1.clone();
println!("克隆: {:?}", s2);
// PartialEq: 比较
println!("相等: {}", s1 == s2); // true
}
7.7 业务场景示例
用户认证系统
#[derive(Debug)]
struct User {
id: u64,
username: String,
email: String,
password_hash: String,
is_active: bool,
login_attempts: u32,
}
impl User {
fn new(id: u64, username: &str, email: &str, password: &str) -> Self {
Self {
id,
username: username.to_string(),
email: email.to_string(),
password_hash: hash_password(password),
is_active: true,
login_attempts: 0,
}
}
fn authenticate(&mut self, password: &str) -> bool {
if !self.is_active {
return false;
}
if hash_password(password) == self.password_hash {
self.login_attempts = 0;
true
} else {
self.login_attempts += 1;
if self.login_attempts >= 5 {
self.is_active = false;
println!("账户 {} 因多次失败被锁定", self.username);
}
false
}
}
fn display_info(&self) {
println!("用户信息:");
println!(" ID: {}", self.id);
println!(" 用户名: {}", self.username);
println!(" 邮箱: {}", self.email);
println!(" 状态: {}", if self.is_active { "活跃" } else { "锁定" });
}
}
// 模拟密码哈希
fn hash_password(password: &str) -> String {
// 实际项目中使用 bcrypt 或 argon2
format!("hashed_{}", password)
}
fn main() {
let mut user = User::new(1, "alice", "[email protected]", "secret123");
user.display_info();
println!("\n认证测试:");
println!("正确密码: {}", user.authenticate("secret123"));
println!("错误密码: {}", user.authenticate("wrong"));
for _ in 0..4 {
user.authenticate("wrong");
}
println!("锁定后认证: {}", user.authenticate("secret123"));
user.display_info();
}
链式配置构建器
#[derive(Debug)]
struct HttpClient {
base_url: String,
timeout_secs: u64,
max_retries: u32,
headers: Vec<(String, String)>,
follow_redirects: bool,
}
impl HttpClient {
fn new(base_url: &str) -> Self {
Self {
base_url: base_url.to_string(),
timeout_secs: 30,
max_retries: 3,
headers: Vec::new(),
follow_redirects: true,
}
}
fn timeout(mut self, secs: u64) -> Self {
self.timeout_secs = secs;
self
}
fn max_retries(mut self, n: u32) -> Self {
self.max_retries = n;
self
}
fn header(mut self, key: &str, value: &str) -> Self {
self.headers.push((key.to_string(), value.to_string()));
self
}
fn follow_redirects(mut self, follow: bool) -> Self {
self.follow_redirects = follow;
self
}
fn build(&self) -> String {
format!(
"HttpClient {{ base_url: {}, timeout: {}s, retries: {}, headers: {} }}",
self.base_url, self.timeout_secs, self.max_retries, self.headers.len()
)
}
}
fn main() {
let client = HttpClient::new("https://api.example.com")
.timeout(10)
.max_retries(5)
.header("Authorization", "Bearer token123")
.header("Accept", "application/json")
.follow_redirects(false)
.build();
println!("{}", client);
}
7.8 本章小结
| 要点 | 说明 |
|---|---|
| 结构体定义 | 使用 struct 关键字,字段有名称和类型 |
| 方法 | impl 块中定义,第一个参数为 self/&self/&mut self |
| 关联函数 | 不接收 self,使用 :: 调用,常用于构造 |
| 元组结构体 | 没有字段名,用索引访问 |
| 单元结构体 | 没有字段,用于 trait 实现 |
| 更新语法 | ..other 语法从另一个实例复制字段 |
| derive | 自动实现常用 trait |
扩展阅读
- Rust Book - 结构体 — 官方教程
- Builder Pattern in Rust — 建造者模式
- derive_more crate — 更多 derive 宏