概述

结构体(Struct)和枚举(Enum)是 Rust 中创建自定义类型的主要方式。它们允许你将相关数据组合在一起,并提供类型安全保证。

结构体

定义结构体

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("someusername123"),
        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 user1 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };
    
    // 访问字段
    println!("用户名: {}", user1.username);
    println!("邮箱: {}", user1.email);
    println!("登录次数: {}", user1.sign_in_count);
    println!("是否活跃: {}", user1.active);
}

结构体更新语法

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("someusername123"),
        active: true,
        sign_in_count: 1,
    };
    
    let user2 = User {
        email: String::from("another@example.com"),
        username: String::from("anotherusername567"),
        ..user1  // 使用 user1 的其他字段
    };
    
    println!("用户2: {}", user2.username);
}

元组结构体

struct Color(i32, i32, i32);
struct Point(i32, i32);

fn main() {
    let black = Color(0, 0, 0);
    let origin = Point(0, 0);
    
    println!("颜色: {} {} {}", black.0, black.1, black.2);
    println!("点: {} {}", origin.0, origin.1);
}

类单元结构体

struct AlwaysEqual;

fn main() {
    let subject = AlwaysEqual;
    // 可以实例化,但没有实际数据
}

结构体数据的所有权

struct User {
    username: String,  // 拥有 String 数据
    email: String,
    sign_in_count: u64,
    active: bool,
}

fn main() {
    let user1 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };
    
    let user2 = user1;  // user1 的所有权移动到 user2
    // println!("{}", user1.username);  // 错误!user1 已经失效
    println!("{}", user2.username);
}

使用引用避免所有权转移

struct User {
    username: &str,  // 使用字符串切片
    email: &str,
    sign_in_count: u64,
    active: bool,
}

fn main() {
    let username = String::from("someusername123");
    let email = String::from("someone@example.com");
    
    let user1 = User {
        email: &email,
        username: &username,
        active: true,
        sign_in_count: 1,
    };
    
    println!("用户: {}", user1.username);
}

方法

定义方法

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // 方法
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };
    
    println!("矩形的面积是 {} 平方像素", rect1.area());
}

带有更多参数的方法

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    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 {
        width: 30,
        height: 50,
    };
    
    let rect2 = Rectangle {
        width: 10,
        height: 40,
    };
    
    let rect3 = Rectangle {
        width: 60,
        height: 45,
    };
    
    println!("rect1 能容纳 rect2 吗? {}", rect1.can_hold(&rect2));
    println!("rect1 能容纳 rect3 吗? {}", rect1.can_hold(&rect3));
}

关联函数

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            height: size,
        }
    }
}

fn main() {
    let sq = Rectangle::square(3);
    println!("正方形: {} x {}", sq.width, sq.height);
}

多个 impl 块

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

impl Rectangle {
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };
    
    println!("面积: {}", rect1.area());
}

枚举

定义枚举

enum IpAddr {
    V4(String),
    V6(String),
}

fn main() {
    let home = IpAddr::V4(String::from("127.0.0.1"));
    let loopback = IpAddr::V6(String::from("::1"));
}

枚举变体中的数据

enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

fn main() {
    let home = IpAddr::V4(127, 0, 0, 1);
    let loopback = IpAddr::V6(String::from("::1"));
}

枚举方法

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

impl Message {
    fn call(&self) {
        // 在这里定义方法体
    }
}

fn main() {
    let m = Message::Write(String::from("hello"));
    m.call();
}

Option 枚举

Option 是标准库中定义的枚举,用于表示可能存在或不存在的值:

enum Option<T> {
    Some(T),
    None,
}

使用示例:

fn main() {
    let some_number = Some(5);
    let some_string = Some("a string");
    let absent_number: Option<i32> = None;
    
    println!("some_number: {:?}", some_number);
    println!("some_string: {:?}", some_string);
    println!("absent_number: {:?}", absent_number);
}

Option 与模式匹配:

fn main() {
    let some_number = Some(5);
    
    match some_number {
        Some(i) => println!("值是: {}", i),
        None => println!("没有值"),
    }
}

Option 的优势

fn main() {
    // 有 Option
    let x: Option<i8> = Some(5);
    let y: Option<i8> = Some(6);
    
    // 错误!不能直接相加
    // let sum = x + y;
    
    // 需要处理 None 的情况
    let sum = match x {
        Some(i) => match y {
            Some(j) => Some(i + j),
            None => None,
        },
        None => None,
    };
    
    println!("和: {:?}", sum);
}

match 控制流

基本 match

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}

fn main() {
    let coin = Coin::Penny;
    println!("硬币价值: {} 分", value_in_cents(coin));
}

绑定值

enum UsState {
    Alabama,
    Alaska,
    // 等等...
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),  // 包含 UsState 值
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => {
            println!("州 quarter 来自 {:?}", state);
            25
        }
    }
}

fn main() {
    let coin = Coin::Quarter(UsState::Alaska);
    println!("硬币价值: {} 分", value_in_cents(coin));
}

匹配 Option

fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i + 1),
    }
}

fn main() {
    let five = Some(5);
    let six = plus_one(five);
    let none = plus_one(None);
    
    println!("five: {:?}", five);
    println!("six: {:?}", six);
    println!("none: {:?}", none);
}

匹配穷尽性

fn main() {
    let some_u8_value = 0u8;
    
    match some_u8_value {
        1 => println!("一"),
        3 => println!("三"),
        5 => println!("五"),
        7 => println!("七"),
        _ => println!("其他数字"),  // 匹配所有其他情况
    }
}

if let 简洁控制流

fn main() {
    let some_value = Some(5);
    
    // 使用 match
    match some_value {
        Some(x) => println!("值是: {}", x),
        _ => println!("没有值"),
    }
    
    // 使用 if let(更简洁)
    if let Some(x) = some_value {
        println!("值是: {}", x);
    }
}

while let 条件循环

fn main() {
    let mut optional = Some(0);
    
    while let Some(i) = optional {
        if i > 5 {
            println!("大于 5,退出");
            optional = None;
        } else {
            println!("i 是 {:?},尝试 again", i);
            optional = Some(i + 1);
        }
    }
}

实际应用示例

创建一个简单的图形库

#[derive(Debug)]
enum Shape {
    Circle { radius: f64 },
    Rectangle { width: f64, height: f64 },
    Triangle { base: f64, height: f64 },
}

impl Shape {
    fn area(&self) -> f64 {
        match self {
            Shape::Circle { radius } => std::f64::consts::PI * radius * radius,
            Shape::Rectangle { width, height } => width * height,
            Shape::Triangle { base, height } => 0.5 * base * height,
        }
    }
}

fn main() {
    let shapes = vec![
        Shape::Circle { radius: 5.0 },
        Shape::Rectangle { width: 10.0, height: 5.0 },
        Shape::Triangle { base: 8.0, height: 6.0 },
    ];
    
    for shape in &shapes {
        println!("形状: {:?}, 面积: {:.2}", shape, shape.area());
    }
}

状态机示例

#[derive(Debug)]
enum State {
    Locked,
    Open,
}

#[derive(Debug)]
enum Event {
    Coin,
    Push,
}

fn transition(state: State, event: Event) -> State {
    match (state, event) {
        (State::Locked, Event::Coin) => State::Open,
        (State::Open, Event::Push) => State::Locked,
        _ => state,
    }
}

fn main() {
    let mut state = State::Locked;
    println!("初始状态: {:?}", state);
    
    state = transition(state, Event::Coin);
    println!("投入硬币后: {:?}", state);
    
    state = transition(state, Event::Push);
    println!("推门后: {:?}", state);
}

总结

本教程详细介绍了 Rust 的结构体与枚举:

  1. 结构体

    • 定义和创建结构体
    • 访问结构体字段
    • 结构体更新语法
    • 元组结构体和类单元结构体
    • 结构体数据的所有权
  2. 方法

    • 定义方法
    • &self 参数
    • 带有更多参数的方法
    • 关联函数
    • 多个 impl 块
  3. 枚举

    • 定义枚举
    • 枚举变体中的数据
    • 枚举方法
    • Option 枚举
    • Option 的优势
  4. match 控制流

    • 基本 match
    • 绑定值
    • 匹配 Option
    • 匹配穷尽性
    • if let 简洁控制流
    • while let 条件循环

结构体和枚举是 Rust 中创建自定义类型的基础,掌握它们对于编写 Rust 代码至关重要。它们与模式匹配结合,提供了强大的类型安全保证。

下一步

在下一教程中,我们将学习 Rust 的错误处理,包括:

  • Result 类型

  • panic! 宏

  • 错误处理的最佳实践

  • 自定义错误类型

继续学习 Rust,掌握这门强大语言的更多特性!