暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Rust 简易笔记(5)—— Enum枚举

沛沛聊AI 2021-10-24
1762
本篇是Rust简易笔记的第五篇

     感谢大家关注!

▼▼▼

关于Rust的前景、优势等不再赘述,这篇主要讲Rust的Enum特性以及pattern匹配



本系列将以“笔记”作为撰文形式,追求简洁明了,概念随时配合代码。如果行文过程中没有讲清楚的地方,欢迎后台私信。如有需要(请后台告诉我),Rust笔记系列更新完成后会再写一份详细的Rust图文教程。





01

总览

Rust的enums像是其他函数式语言的algebraic data types代数数据类型。





02

定义枚举类型和构造实例


/**
* 1. StrV4和StrV6是相同类型,可以作为同类型参数传入函数中
* 2. 直接在enum中把具体的值类型写到每个枚举类型里,写法更简洁(不必再增加一个结构体或其他代码)
* 3. 枚举类型里可以包含不同类型和数量的值
*/
#[derive(Debug)]
enum IpAddrKind {
   StrV4(String),
   StrV6(String),
   VarV4(u8, u8, u8, u8),
}

fn basic_test() {
   let home = IpAddrKind::StrV4(String::from("127.0.0.1"));
   let loopback = IpAddrKind::StrV6(String::from("::1"));
   let local = IpAddrKind::VarV4(192, 168, 0, 1);
   route(home);
   route(loopback);
   route(local);
}

fn route(ip_kind: IpAddrKind) {
   println!("The Ip address is: {:?}", ip_kind);
}

输出为:

The Ip address is: StrV4("127.0.0.1")
The Ip address is: StrV6("::1")
The Ip address is: VarV4(192, 168, 0, 1)

可见枚举类型的特点为:

  1. 不同的枚举值可以作为相同参数类型传入函数中。

  2. 可以直接在enum中把值类型和枚举类型结合起来,写法很简洁。

  3. 同一个enum里可以有不同的枚举类型,分别对应不同类型和数量的值。


  • 枚举类型中可以关联的类型

枚举类型中可以不关联数据,也可以关联String和基本数据类型,还可以关联结构体和枚举。如果关联自身的话,需要使用关键字Box, Rc或&。

枚举类型同样可以使用impl定义method。

#[derive(Debug)]
enum Message {
   Quit, // has no data associated with it at all.
   Move { x: i32, y: i32}, // includes an anonymous struct inside it.
   Write(String),// includes a single String.
   ChangeColor(i32, i32, i32), // includes three i32 values.
   Nested(Box<Message>), // insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Message` representable
}

impl Message {
   fn previous_message(&self) {
       println!("previous message is: {:?}", self);
  }
}

fn message_test() {
   let mut m = Message::Write(String::from("hello"));
   m.previous_message();
   m = Message::Move {x: 45, y: 33};
   m.previous_message();
   m = Message::ChangeColor(0, 196, 251);
   m.previous_message();
   m = Message::Quit;
   m.previous_message();
   m = Message::Nested(Box::from(m));
   m.previous_message();
}

输出:

previous message is: Write("hello")
previous message is: Move { x: 45, y: 33 }
previous message is: ChangeColor(0, 196, 251)
previous message is: Quit
previous message is: Nested(Quit)


  • None和Option

Rust没有NULL类型。在需要表示无效值的时候用系统提供的Option枚举。

/// The `Option` type. See [the module level documentation](self) for more.
#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
#[rustc_diagnostic_item = "option_type"]
#[stable(feature = "rust1", since = "1.0.0")]
pub enum Option<T> {
   /// No value
   #[lang = "None"]
   #[stable(feature = "rust1", since = "1.0.0")]
   None,
   /// Some value `T`
   #[lang = "Some"]
   #[stable(feature = "rust1", since = "1.0.0")]
   Some(#[stable(feature = "rust1", since = "1.0.0")] T),
}

如果赋值为None的话,需要指明T的类型。用法如下:

fn option_test() {
   let some_number = Some(5);
   let some_string = Some("a string");
   let absent_number: Option<i32> = None; // 需要指明类型
   println!("some number: {:?}", some_number);
   println!("some string: {:?}", some_string);
   println!("absent number: {:?}", absent_number);
}

输出:

some number: Some(5)
some string: Some("a string")
absent number: None

这样的好处是在编译期即可防止误把未赋值的无效值参与运算,因为T
Option<T>
不是同一类型:

let x: i8 = 5;
let mut y: Option<i8> = None;
let sum = x + y; // error: no implementation for `i8 + Option<i8>`

Rust编译器认为i8
Optioin<i8>
不是同一类型,因此无法运算。

对其稍作修改:

let x: i8 = 5;
let mut y: Option<i8> = None;
y = Some(6);
let sum = x + y.expect("y is none");
println!("sum: {}", sum);

正常输出:

sum: 11
  • 假设我们忘记第三行的给y赋值,编译器会报错:thread 'main' panicked at 'y is none'
    以提醒我们y是none。

如果某个变量是通过Option声明的,表示它可能会存在为Null的情况,需要在使用时多加注意;反之,可以大胆放心地对它使用,因为它一定不会是Null。





03

match表达式

类似switch语句,从上到下逐个匹配。pattern可以是字面值,变量名或通配符等。匹配结果可以是单个返回值(返回值可以是任意类型),也可以是多条执行语句加一个返回值:

fn match_test() {
   let coin = Coin::Penny;
   println!("The value in coin: {}", value_in_cents(coin));
}

enum Coin {
   Penny,
   Nickel,
   Dime,
   Quarter,
}

fn value_in_cents(coin: Coin) -> u8 {
   match coin {
       Coin::Penny => {
           println!("Lucky penny");
           1
      },
       Coin::Nickel => 5,
       Coin::Dime => 10,
       Coin::Quarter => 25,
  }
}

输出:

Lucky penny
The value in coin: 1


  • pattern中绑定值

比如将上述代码中的Quarter
绑定上另外一个枚举类型UsState
,那么实际的匹配pattern会相应地变为Coin::Quarter(state)
,且传入参数变为Coin::Quarter(UsState::Alabama)
。这样一来,就可以在match表达式对应的pattern中拿到变量state
的值了。如下例:

fn match_test() {
   let coin = Coin::Quarter(UsState::Alabama);
   println!("The value in coin: {}", value_in_cents(coin));
}

#[derive(Debug)]
enum UsState {
   Alabama,
   Alaska,
}

enum Coin {
   Penny,
   Nickel,
   Dime,
   Quarter(UsState),
}

fn value_in_cents(coin: Coin) -> u8 {
   match coin {
       Coin::Penny => {
           println!("Lucky penny");
           1
      },
       Coin::Nickel => 5,
       Coin::Dime => 10,
       Coin::Quarter(state) => {
           println!("State quarter from {:?}!", state);
           25
      },
  }
}

输出:

State quarter from Alabama!
The value in coin: 25


  • pattern匹配Option<T>

如下列:

fn plus_one(x: Option<i32>) -> Option<i32> {
   match x {
       None => None,
       Some(i) => Some(i + 1),
  }
}

fn match_option() {
   let five = Some(5);
   println!("five plus one is: {:?}", plus_one(five));
   println!("None plus one is: {:?}", plus_one(None));
}

输出:

five plus one is: Some(6)
None plus one is: None

如果我们在match中少写了某种情况,比如Coin::Nickel => 5,
None => None,
,编译器会以报错提醒我们:

match coin {
    ^^^^ pattern `Nickel` not covered
match x {
    ^ pattern `None` not covered

当然,也有类似default
的“其他”pattern,即placeholder,用下划线表示:

fn match_placeholder() {
   let some_u8_value = 0u8;
   match some_u8_value {
       1 => println!("one"),
       3 => println!("three"),
       _ => println!("other"),
  }
}

输出:

other


  • if let 匹配

只匹配一个pattern,其他与match一致。有点像if constexpr
,但是在比较时只有一个等号:

fn if_let_test() {
   let coin = Coin::Quarter(UsState::Alaska);
   let mut count = 0;
   if let Coin::Quarter(state) = coin {
       println!("State quarter from {:?}!", state);
  } else {
       count += 1;
  }
}

输出:

State quarter from Alaska!




以上是Rust的Ownership特性,配套代码见:

    https://github.com/ds1231h/RustSimpleCode 


    src/enums.rs


    Rust的安装、编译与运行参加本系列第一篇:

    Rust 简易笔记(1)—— 安装与配置







    想要了解最新鲜技术科普吗?
    想要学习
    最硬核的技术干货吗?

    点击下方链接
    关!注!公!众!号!

    了解更多有趣的硬核干货
    ▼▼▼

    撰文:沛沛


    文章转载自沛沛聊AI,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论