强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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("someone@example.com"),
        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("user@example.com"),
        username: String::from("user"),
        active: true,
        sign_in_count: 1,
    };

    user.email = String::from("new@example.com");
    println!("新邮箱: {}", user.email);
}

注意: Rust 不允许只将结构体的某个字段标记为可变。可变性是整个绑定的属性。

结构体更新语法

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

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

    // 使用 ..user1 语法创建新实例,其余字段从 user1 获取
    let user2 = User {
        email: String::from("other@example.com"),
        ..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", "alice@example.com", "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

扩展阅读

  1. Rust Book - 结构体 — 官方教程
  2. Builder Pattern in Rust — 建造者模式
  3. derive_more crate — 更多 derive 宏