定义枚举
enum IpAddrKind {
V4,
V6,
}
枚举值
枚举的成员位于其标识符的命名空间中,并使用两个冒号分开。
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
fn route(ip_type: IpAddrKind) { }
route(IpAddrKind::V4);
route(IpAddrKind::V6);
enum IpAddrKind {
V4,
V6,
}
struct IpAddr {
kind: IpAddrKind,
address: String,
}
let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};
直接将数据附加到枚举的每个成员上
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
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 包含一个匿名结构体。
- Write 包含单独一个 String 。
- ChangeColor 包含三个 i32 。
struct QuitMessage; // 类单元结构体
struct MoveMessage {
x: i32,
y: i32,
}
struct WriteMessage(String); // 元组结构体
struct ChangeColorMessage(i32, i32, i32); // 元组结构体
这里回顾下相关概念
元组:let tup: (i32, f64, u8) = (500, 6.4, 1);
类单元结构体:struct QuitMessage;
元组结构体:struct Color(i32, i32, i32);
元组只有类型没有名称,且都是以()定义,而结构体都是以{}定义,除了元组结构体。
C++的枚举只支持值类型的,Rust的枚举应该是支持了更多类型。
# enum Message {
# Quit,
# Move { x: i32, y: i32 },
# Write(String),
# ChangeColor(i32, i32, i32),
# }
#
impl Message {
fn call(&self) {
// 在这里定义方法体
}
}
let m = Message::Write(String::from("hello"));
m.call();
这里的write也没看见实现,反正只是返回一个枚举对象,暂时不管。
前面结构体已经用到过self,等同于c++的this理解,默认的unmut等同于C++的成员函数后面加const,且一般传递&self等同于传递的是this对象本身的引用,只传self因为所有权变更只在特定情况下才能使用。
Option 枚举和其相对于空值的优势
Option 是标准库定义的另一个枚举。 Option 类型应用广泛因为它编码了一个非常普遍的场景,即一个值要么有值要么没值。
Rust 并没有很多其他语言中有的空值功能。
空值概念上讲是一个因为某种原因目前无效或缺失的值。由于空值引起的程序问题并非空值本身存在问题,而在于具体的实现。
Rust 并没有空值,不过它确实拥有一个可以编码存在或不存在概念的枚举。这个枚举是 Option
enum Option<T> {
Some(T),
None,
}
Some(T) 和 None 仍是 Option
目前需要知道的就是
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
如果使用 None 而不是 Some ,需要告诉 Rust Option
过 None 值无法推断出 Some 成员保存的值的类型。
当有个 None 值时,在某种意义上,它跟空值具有相同的意义:并没有一个有效的值。那么, Option
简而言之,因为 Option
许像一个肯定有效的值那样使用 Option
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = x + y;//错误,类型不匹配
^ no implementation for `i8 + std::option::Option<i8>`
错误信息意味着 Rust 不知道该如何将 Option
当在 Rust 中拥有一个像 i8 这样类型的值时,编译器确保它总是有一个有效的值。我们可以自信使用而无需做空值检查。只有当使用 Option
为了拥有一个可能为空的值,你必须要显式的将其放入对应类型的 Option
Rust默认定义的变量如果为空则直接编译报错,这是 Rust 的一个经过深思熟虑的设计决策,来限制空值的泛滥以增加 Rust 代码的安全性。
当有一个 Option
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,
}
}
以上语义上基本上是类似C++形式的枚举switch。
=> 运算符将模式和将要运行的代码分开,每一个分支之间使用逗号分隔。如果分支代码较短的话通常不使用大括号。
绑定值的模式
匹配分支的另一个有用的功能是可以绑定匹配的模式的部分值。这也就是如何从枚举成员中
提取值的。
#[derive(Debug)] // 这样可以可以立刻看到州的名称
enum UsState {
Alabama,
Alaska,
// --snip--
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("State quarter from {:?}!", state);
25
},
}
}
value_in_cents(Coin::Quarter(UsState::Alaska))
value_in_cents(Coin::Quarter(UsState::Alaska)) , Coin::Quarter(UsState::Alaska) 作为形参coin的实参。当将值与每个分支相比较时,匹配到Coin::Quarter(state) 。这时, state 绑定的将会是值 UsState::Alaska 。
匹配 Option和匹配 Some(T)
Option
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
匹配是穷尽的
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
Some(i) => Some(i + 1),//错误^ pattern `None` not covered
}
}
Rust 中的匹配是 穷尽的(exhaustive):必须穷举到最后的可能性来使代码有效。
_ 通配符
Rust 也提供了一个模式用于不想列举出所有可能值的场景
let some_u8_value = 0u8;
match some_u8_value {
1 => println!("one"),
3 => println!("three"),
5 => println!("five"),
7 => println!("seven"),
_ => (),
}
_ 模式会匹配所有的值。通过将其放置于其他分支之后, _ 将会匹配所有之前没有指定的
可能的值。 () 就是 unit 值,所以 _ 的情况什么也不会发生。
match 在只关心 一个 情况的场景中可能就有点啰嗦了。为此 Rust 提供了 if let
if let 简单控制流
处理只匹配一个模式的值而忽略其他模式的情况。
let some_u8_value = Some(0u8);
if let Some(3) = some_u8_value {
println!("three");
}
let some_u8_value = Some(0u8);
match some_u8_value {
Some(3) => println!("three"),
_ => (),
}
let coin = Coin::Penny;
let mut count = 0;
if let Coin::Quarter(state) = coin {
println!("State quarter from {:?}!", state);
}
else {
count += 1;
}
Coin::Quarter(state)为枚举成员,coin为实际要比较的实参。