Skip to content

枚举方法

> 学习使用 impl 为枚举定义方法,实现状态机和领域建模。

为枚举定义方法

rust
#[derive(Debug)]
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

impl Message {
    // 实例方法
    fn call(&self) {
        match self {
            Message::Quit => println!("退出"),
            Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
            Message::Write(text) => println!("写入:{}", text),
            Message::ChangeColor(r, g, b) => {
                println!("改变颜色:({}, {}, {})", r, g, b)
            }
        }
    }

    // 带返回值的方法
    fn message_type(&self) -> &str {
        match self {
            Message::Quit => "quit",
            Message::Move { .. } => "move",
            Message::Write(_) => "write",
            Message::ChangeColor(_, _, _) => "change_color",
        }
    }

    // 修改自身的方法
    fn move_to(&mut self, x: i32, y: i32) {
        *self = Message::Move { x, y };
    }
}

fn main() {
    let messages = [
        Message::Quit,
        Message::Move { x: 10, y: 20 },
        Message::Write(String::from("Hello")),
        Message::ChangeColor(255, 0, 0),
    ];

    for msg in &messages {
        msg.call();
    }
}
▶ Run

完整示例

示例 1:交通灯状态机

rust
#[derive(Debug, Clone, Copy)]
enum TrafficLight {
    Red,
    Yellow,
    Green,
}

impl TrafficLight {
    fn next(&self) -> TrafficLight {
        match self {
            TrafficLight::Red => TrafficLight::Green,
            TrafficLight::Green => TrafficLight::Yellow,
            TrafficLight::Yellow => TrafficLight::Red,
        }
    }

    fn action(&self) -> &'static str {
        match self {
            TrafficLight::Red => "停止",
            TrafficLight::Green => "通行",
            TrafficLight::Yellow => "等待",
        }
    }

    fn duration(&self) -> u32 {
        match self {
            TrafficLight::Red => 60,
            TrafficLight::Green => 45,
            TrafficLight::Yellow => 5,
        }
    }
}

fn main() {
    let mut light = TrafficLight::Red;

    println!("交通灯模拟:");
    println!("{:-^30}", "");

    for i in 0..6 {
        println!("第{}次:{:?} - {} ({}秒)",
                 i + 1, light, light.action(), light.duration());
        light = light.next();
    }
}
▶ Run

示例 2:网络请求状态

rust
#[derive(Debug)]
enum RequestState<T> {
    Loading,
    Success(T),
    Error(String),
}

impl<T> RequestState<T> {
    fn is_loading(&self) -> bool {
        matches!(self, RequestState::Loading)
    }

    fn is_success(&self) -> bool {
        matches!(self, RequestState::Success(_))
    }

    fn is_error(&self) -> bool {
        matches!(self, RequestState::Error(_))
    }

    fn map<U, F>(self, f: F) -> RequestState<U>
    where
        F: FnOnce(T) -> U,
    {
        match self {
            RequestState::Loading => RequestState::Loading,
            RequestState::Success(data) => RequestState::Success(f(data)),
            RequestState::Error(e) => RequestState::Error(e),
        }
    }

    fn unwrap_or(self, default: T) -> T {
        match self {
            RequestState::Success(data) => data,
            _ => default,
        }
    }
}

fn main() {
    let loading: RequestState<i32> = RequestState::Loading;
    let success = RequestState::Success(42);
    let error: RequestState<i32> = RequestState::Error("网络错误".to_string());

    println!("loading 状态:{:?}", loading.is_loading());
    println!("success 状态:{:?}", success.is_success());
    println!("error 状态:{:?}", error.is_error());

    let doubled = success.map(|x| x * 2);
    println!("翻倍后:{:?}", doubled);
}
▶ Run

示例 3:简单表达式求值器

rust
#[derive(Debug, Clone)]
enum Expr {
    Number(i32),
    Add(Box<Expr>, Box<Expr>),
    Sub(Box<Expr>, Box<Expr>),
    Mul(Box<Expr>, Box<Expr>),
    Div(Box<Expr>, Box<Expr>),
}

fn eval(expr: Expr) -> i32 {
    match expr {
        Expr::Number(n) => n,
        Expr::Add(a, b) => eval(*a) + eval(*b),
        Expr::Sub(a, b) => eval(*a) - eval(*b),
        Expr::Mul(a, b) => eval(*a) * eval(*b),
        Expr::Div(a, b) => eval(*a) / eval(*b),
    }
}

fn main() {
    // (3 + 5) * 2
    let expr = Expr::Mul(
        Box::new(Expr::Add(
            Box::new(Expr::Number(3)),
            Box::new(Expr::Number(5)),
        )),
        Box::new(Expr::Number(2)),
    );

    println!("(3 + 5) * 2 = {}", eval(expr));  // 16
}
▶ Run

小结

  • 枚举可以像结构体一样用 impl 定义方法
  • 方法内用 match self 处理不同变体
  • matches!(self, Variant) 快速判断是否匹配某变体
  • 泛型枚举 Enum<T> 的方法需要 impl<T> Enum<T>

练习题

详见:练习题