枚举的定义
- ipV4和ipV6使用IpAddrKind来进行枚举
fn main() {
enum IpAddrKind {
V4,
V6,
}
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
}
//定义函数可以获取IpAddrKind
fn route(ip_type: IppAddrKind)
{
}
route(IpAddrKind::V4);
route(IpAddrKind::V6);
- 目前没有实际存储IP地址数据的方法,只知道它的类型,下面代码:
fn main() {
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"),
};
}
- 这段可以改变,将枚举数据放入枚举成员而不是放入结构体。
fn main() {
enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
}
- 同时,这样做还有一个好处,每个成员可以处理不同类型和数量的数据。
fn main() {
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
}
- 同时,标准库也定义了IppAddr,它是将数据放入结构体中,
fn main() {
struct Ipv4Addr {
// --snip--
}
struct Ipv6Addr {
// --snip--
}
enum IpAddr {
V4(Ipv4Addr),
V6(Ipv6Addr),
}
}
- 而标准是将成员中的数据放入到不同的结构体中,然后将结构体放入枚举中。
- 可以将任意类型的数据放入枚举成员中:例如字符串、数字类型或者结构体。甚至可以包含另一个枚举!
fn main() {
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
}
- Quit 没有关联任何数据。
- Move 包含一个匿名结构体。
- Write 包含单独一个 String。
- ChangeColor 包含三个 i32。
fn main() {
struct QuitMessage; // 类单元结构体
struct MoveMessage {
x: i32,
y: i32,
}
struct WriteMessage(String); // 元组结构体
struct ChangeColorMessage(i32, i32, i32); // 元组结构体
}
- 同时可以使用不同的结构体存储上述变量。
impl
关键字也可以用来定义枚举。
fn main() {
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();
}
- 这里使用&self使用self中的值。
Option枚举
rust中不存在空值,但是存在可以编码存在或者不存在概念的枚举,是Option,定义在标准库之下
fn main() {
enum Option<T> {
Some(T),
None,
}
}
- option被包含在prelude之中,不需要显式引出,它的成员也是这样,可以直接使用
some
和None
。 - 是一个未讲到的语法,是一个泛型类型参数,代表着
some
和None
可以包含任何类型的数据。
#![allow(unused)]
fn main() {
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
}
- 如果使用
None
而不是Some
,需要告诉 RustOption<T>
是什么类型的,因为编译器只通过None
值无法推断出Some
成员保存的值的类型。 - 当有一个
Some
值时,我们就知道存在一个值,而这个值保存在Some
中。当有个None
值时,在某种意义上,它跟空值具有相同的意义:并没有一个有效的值。
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = x + y;
- 这段代码是无法运行的,因为尝试将option和i8相加。
- RUST只有在使用Option时需要担心是否有值。
- 在
Option<T>
运算之前需要先将其转换为T
类型。 - 紧接着的match表达式就是这样一个处理枚举的控制流结构。
match控制流运算符
- 和
if、loop
等相同,match
也是控制流。 match
将一个值和一系列模式比较,匹配出相应的模式执行相应代码。有点像是case
函数。
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,
}
}
- 这里match和if最大的区别是if返回一个bool值,而match可以是任何类型的。
- match会从上到下,执行分支,可以拥有多个分支。如果要在分支中运行多行代码,需要使用大括号。
match coin
{
Coin::Penny =>{
println!("Lucky penny!");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
绑定值match控制流的模式
- 在25美分上有彩蛋,为州的名字。
#[derive(Debug)] // 这样可以立刻看到州的名称
enum UsState {
Alabama,
Alaska,
// --snip--
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
fn main() {}
- 之后对state中对应州值的函数代码:
#[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
}
}
}
fn main() {
value_in_cents(Coin::Quarter(UsState::Alaska));
}
- 这里要注意的是,不需要再对UsState进行重新创建变量,而是直接使用了state这个关键词。
匹配Option
- 之前是使用
some
取出内部的T
,同时也可以用match
处理Option<T>
!。 - 编写函数获取Option,如果有值,值加一。如果没有值,返回None。
fn main() {
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);
}
匹配Some(T)
- 接下来是一段官方文档
Rust中匹配的有穷性
- rust中匹配必须是有穷尽的,需要将所有的可能结果都写进去。
~官方文档:
通配模式和_占位符
- 一个例子:对3和7采取操作,对其他行为采取默认操作。
fn main() {
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
other => move_player(other),
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn move_player(num_spaces: u8) {}
}
- 命名一个
other
变量,它的分支代码通过其传递给move_player
函数。 - 即使没有列出
u8
所有值,也能正常编译,同时,最后一个模式将匹配所有未被列出的特殊值,为通配模式。 - 同时,也可以使用’_'来匹配任意值,但不绑定该值,这个符号代表不想使用通配模式获取的值。
fn main() {
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => reroll(),
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn reroll() {}
}
- 可以在最后一个分支使用:
fn main() {
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => (),
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
}
if let 简单控制流
if let
可以用来处理只匹配一个模式的值,忽略其他模式的情况。
fn main() {
let some_u8_value = Some(0u8);
match some_u8_value {
Some(3) => println!("three"),
_ => (),
}
}
- 在使用if let之后,代码变成了:
fn main() {
let some_u8_value = Some(0u8);
if let Some(3) = some_u8_value {
println!("three");
}
}
if let
是match
的一个语法糖,同时其中可以添加一个else
,这个else
对用着match
中的_
。
接着是一个官方文档:
- 巧妙利用
match
和if let
会事半功倍。