枚举方法
> 学习使用 impl 为枚举定义方法,实现状态机和领域建模。
为枚举定义方法
rust
▶ Run#[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();
}
}完整示例
示例 1:交通灯状态机
rust
▶ Run#[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();
}
}示例 2:网络请求状态
rust
▶ Run#[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);
}示例 3:简单表达式求值器
rust
▶ Run#[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
}小结
- 枚举可以像结构体一样用
impl定义方法 - 方法内用
match self处理不同变体 matches!(self, Variant)快速判断是否匹配某变体- 泛型枚举
Enum<T>的方法需要impl<T> Enum<T>
练习题
详见:练习题