第八章 枚举的定义

张开发
2026/4/21 1:45:31 15 分钟阅读

分享文章

第八章 枚举的定义
枚举类的定义是使用关键字enum后面跟着枚举类的名称再之后使用花括号将枚举的内容包含在内每个枚举内容使用分号进行分割。例如下面使用ip地址的枚举类型进行展示enum IpAddrKind { V4, V6, }这个枚举类的含义是IP地址现在有两个版本一个IPV4版本一个是IPV6版本。枚举的值如何使用枚举类型和枚举的值呢我们可以将枚举类型的值赋给某个变量例如let four IpAddrKind::V4; let six IpAddrKind::V6;需要注意的是必须使用枚举类型名称后面跟着双冒号然后加上枚举的内容值。下面我们创建一个函数它使用这个枚举类型作为参数fn route(ip_kind: IpAddrKind) {}在调用这个函数时可以将枚举类型值或者它的变量名传递给这个函数route(IpAddrKind::V4); route(IpAddrKind::V6); route(four); route(six);上面的示例中我们只知道对应的枚举类型但是并不知道该类型的值如果需要将类型和值保存在一起我们可以在创建一个结构体同时保存类型和值例如下面的代码#[derive(Debug)] enum IpAddrKind { V4, V6, } #[derive(Debug)] struct IpAddr { kind: IpAddrKind, address: String, } fn main() { let home IpAddr { kind: IpAddrKind::V4, address: 127.0.0.1.into(), }; let loopback IpAddr { kind: IpAddrKind::V6, address: ::1.into(), }; println!({home:#?}); println!({loopback:#?}); }通过上面的代码我们发现使用一个枚举类型还需要同时再定义一个结构体有点麻烦能不能将枚举的类型和值定义在一起呢当然可以了。我们在看看下面的代码会大大的精简以上的代码#[derive(Debug)] enum IpAddr { V4(String), V6(String), } fn main() { let addr1 IpAddr::V4(172.16.12.1.into()); let addr2 IpAddr::V6(::1.into()); println!({addr1:#?}); println!({addr2:#?}); }在枚举的定义时使用小括号将值类型和枚举类型关联起来。在枚举实例化时小括号内直接写入值作为它的参数看上去有点像有参数的函数。如果我们使用4个u8类型的数字来表示为IPV4的地址如果使用结构体则有些难办如果使用枚举类型就会很容易实现例如#[derive(Debug)] enum IpAddr { V4(u8,u8,u8,u8), V6(String), } fn main() { let addr1 IpAddr::V4(192,168,0,1); let addr2 IpAddr::V6(::1.into()); println!({addr1:#?}); println!({addr2:#?}); }Rust的标准库中Ip地址的定义和我们的类似但是比我们的复杂它的枚举类型的值是一个结构体因此它的值可以定义为多种形式struct Ipv4Addr { // --snip-- } struct Ipv6Addr { // --snip-- } enum IpAddr { V4(Ipv4Addr), V6(Ipv6Addr), }下面来看一个更加复杂的示例它有各种不同类型值的枚举enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), }这个枚举类型有四种不同的类型和值Quit无数据值Move有两个i32的值类型用于表示坐标值Write有一个字符串的值类型ChangeColor有3个i32值类型分别表示为颜色的RGB值如果要使用结构体则要复杂的多而且需要用到三种不同的结构体struct QuitMessage; // 单元结构体 struct MoveMessage { // 普通结构体 x: i32, y: i32, } struct WriteMessage(String); // 元祖结构体 struct ChangeColorMessage(i32, i32, i32); // 元祖结构体使用结构体需要有四个它们在逻辑上是独立的不像枚举类使用一个枚举类型就可以表达清楚这四种类型。枚举类型和结构体如果需要定义方法或者关联函数它们使用同一套规则例如下面的Message的call方法enum Message { Quit, Move {x:i32,y:i32}, Write(String), ChangeColor(i32,i32,i32), } impl Message { fn call(self){ match self { Message::Write(text) println!({text}), _ return, } } } fn main() { let message Message::Write(Hello World!.into()); message.call(); }如果需要获取枚举类型中的值需要使用match和元祖的解构来获取枚举中的值也可以使用let 赋值语言来获取枚举类型中的值例如impl Message { fn call(self) { if let Message::Write(text) self { println!({}, text); } else { println!(Not a Write message); } } }Option枚举类型Option也是一个枚举类型它是由标准库提供的一个枚举类型用于表示空值或者是有着某种类型的值。它的使用场景非常广泛用于替换其他语言中的null它在标准库中的定义如下所示enum OptionT { None, Some(T), }Option类型使用广泛也经常使用因此它已经包含在了预编译库中你不需要显式的引入而且它的成员也包含在了预编译库内你可以直接使用Some和None而无需增加前缀“Option::”。Some可以保存任意类型的数据。例如let some_number Some(5); let some_char Some(e); let absent_number: Optioni32 None;some_number的类型是Optioni3some_char的类型是OptioncharRust可以根据它的值自动推断它们的类型但是absent_number它的值是None因此无法推断它的类型因此必须显式注明它的类型为Optioni32。由于OptionT和T是不同的类型因此它们不可以直接进行操作否则系统会报错fn main() { let x:i8 5; let y:Optioni8 Some(5); let sum x y; } 运行上面的代码系统报错如果需要解决上面的问题可以使用下面的代码将Some(5)中的值取出来fn main() { let x:i8 5; let y:Optioni8 Some(5); let sum x y.unwrap(); println!(sum:{sum}); }或者使用下面的代码fn main() { let x:i8 5; let y:Optioni8 Some(5); let sum x y.unwrap_or(0); println!(sum:{sum}); }

更多文章