Rust中的enum(枚举)与C/C++中的enum截然不同:它们能够存储关联数据,非常适合建模状态和错误。借助它们,可以构建类型安全的有限状态机,处理各种Option/Result,实现“和类型”模式。历史上,类似的结构在函数式语言中用于描述具有严格分隔的实体变体。
问题: 需要表达所有状态的变体,确保每个处理情况都是必要的,且无法意外跳过分支。项目范围内的错误很难在没有这种表意结构的情况下进行类型化。
解决方案: 具有关联数据和模式匹配的enum提供了控制——每个分支都经过编译器检查,确保穷尽性。此外,Result和Option已经实现了大量辅助方法。
代码示例:
enum NetworkState { Disconnected, Connecting(u32), // 尝试编号 Connected(String), Error(String), } fn print_state(state: NetworkState) { match state { NetworkState::Disconnected => println!("Net: disconnected"), NetworkState::Connecting(count) => println!("Net: connecting (attempt {})", count), NetworkState::Connected(addr) => println!("Net: connected to {}", addr), NetworkState::Error(msg) => println!("Net error: {}", msg), } }
关键特点:
可以部分处理enum的分支而不使用_吗?
编译器禁止未覆盖的情况对于非穷尽enum,但如果使用_,未处理的分支将被“吞噬”。在临床关键分支中应避免使用_,以确保未来更改不会被忽视。
在什么情况下,关联值是引用而在什么情况下是复制的?
在模式匹配时,关联数据默认是移动的(move)。如果只需要查看,请使用引用:
match &state { NetworkState::Connected(addr) => println!("by ref: {}", addr), _ => {} }
一个结构中可以使用两个带有重叠名称的enum吗?
可以,但变体的名称需要使用enum前缀。这可以避免冲突并使代码自文档化(例如,Status::Ok与NetworkState::Ok)。
在代码中,Result<T, E>的处理总是通过_ =>进行catch-all,新错误(在扩展enum时)被忽略——发生静默失败错误。
优点:
缺点:
使用穷尽性匹配,每个Enum变体都被明确处理,或者在不存在稳定行为的分支中panic。
优点:
缺点: