讲解

结构体(struct)把相关的命名字段组合成一个自定义类型,类似其他语言里的「记录」或「纯数据对象」。定义用 struct 关键字加花括号列出字段名和类型;创建实例时给每个字段赋值(顺序无所谓),用点号访问字段。实例整体可变或不可变——Rust 不支持只把某几个字段标成 mut,可变性属于整个实例。

两个高频语法糖。字段初始化简写:当局部变量名和字段名相同时,build_user 里写 name 而不是 name: name。结构体更新语法:基于已有实例创建新实例时,..base 表示「其余字段从 base 搬过来」——注意这是移动语义,base 里 String 类型的字段被搬走后,base 就不能再整体使用了(Copy 类型的字段仍可读)。

结构体还有两种变体。元组结构体 struct Color(i32, i32, i32) 给「几个值的组合」起名字,字段靠位置区分,适合坐标、颜色这类语义明确的短组合;单元结构体 struct Marker 没有任何字段,常用于只承载行为(实现 Trait)的类型。另外提醒:想让结构体实例能被 println!("{:?}") 调试打印,在定义上加 #[derive(Debug)] 属性即可,编译器会自动生成实现。

示例

定义结构体、字段简写与 Debug 打印:

#[derive(Debug)]
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

fn build_user(username: String, email: String) -> User {
    User {
        username, // 字段初始化简写
        email,
        sign_in_count: 1,
        active: true,
    }
}

fn main() {
    let u = build_user(String::from("小林"), String::from("lin@example.com"));
    println!("{:?}", u);
    assert!(u.active);
    assert_eq!(u.sign_in_count, 1);
    assert_eq!(u.username, "小林");
}

更新语法与元组结构体:

#[derive(Debug)]
struct Config {
    host: String,
    port: u16,
    retries: u32,
}

// 元组结构体:有名字的元组
struct Color(u8, u8, u8);

fn main() {
    let default = Config {
        host: String::from("localhost"),
        port: 8080,
        retries: 3,
    };

    // 更新语法:port 改了,其余从 default 移动过来
    let prod = Config {
        port: 443,
        ..default
    };
    // default 的 host 字段已被移动,default 整体不能再用
    assert_eq!(prod.host, "localhost");
    assert_eq!(prod.port, 443);
    assert_eq!(prod.retries, 3);

    let black = Color(0, 0, 0);
    println!("黑色 RGB:({}, {}, {})", black.0, black.1, black.2);
    assert_eq!(black.0, 0);
}

常见坑

  • 想只 mut 一个字段:可变性属于整个实例,let mut u 之后所有字段都能改;没有字段级可变性语法。
  • 更新语法后还用旧实例:..base 会移动 base 的非 Copy 字段,之后 println!("{:?}", base) 编译报错——这是所有权规则在起作用。
  • 在结构体里直接存引用:struct S { s: &str } 需要生命周期标注(第 21 章),入门阶段先用 String 让结构体拥有数据。
  • 打印结构体报错:{:?} 要求实现 Debug;忘了加 #[derive(Debug)] 会收到编译器提示,照着加即可。

小结

struct 组合命名字段;字段简写和 ..base 更新语法让构造更顺;元组结构体给组合起名;可变性整体生效;derive(Debug) 解锁调试打印。数据有了,下一章给数据配行为:方法。