Rust语言枚举类型浅学随笔

内容分享1天前发布
0 1 0

Rust enum是典型的代数数据类型系统ADT, 可以传参,可以组合等,其是Rust语言灵魂要素之一,如:Option<T>, Result<T>,而且Rust语言的枚举变体可以携带不同类型的数据,就像一个结构体,所以提供match if let等等模式匹配和解构的强劲特性,故此可以提供更加强劲,更高层次的抽象封装能力,其强劲远超c语言的enum.

enum 定义

enum WebEvent {
    // An `enum` may either be `unit-like`,
    //类似于:struct PageLoad ; // 一个empty struct。
    PageLoad,
    PageUnload,
    // like tuple structs,
    //类似于一个tuple struct。
    KeyPress(char),
    Paste(String),
    // or c-like structures.
    //类似于c structure。
    Click { x: i64, y: i64 },
}

// A function which takes a `WebEvent` enum as an argument and
// returns nothing.
fn inspect(event: WebEvent) {
    match event { //enum 模式匹配。
        WebEvent::PageLoad => println!("page loaded"),
        WebEvent::PageUnload => println!("page unloaded"),
        // Destructure `c` from inside the `enum`.
        WebEvent::KeyPress(c) => println!("pressed '{}'.", c),
        WebEvent::Paste(s) => println!("pasted "{}".", s),
        // Destructure `Click` into `x` and `y`.
        WebEvent::Click { x, y } => {
            println!("clicked at x={}, y={}.", x, y);
        },
    }
}

fn main() {
    let pressed = WebEvent::KeyPress('x');
    // `to_owned()` creates an owned `String` from a string slice.
    let pasted  = WebEvent::Paste("my text".to_owned());
    let click   = WebEvent::Click { x: 20, y: 80 };
    let load    = WebEvent::PageLoad;
    let unload  = WebEvent::PageUnload;

    inspect(pressed);
    inspect(pasted);
    inspect(click);
    inspect(load);
    inspect(unload);
}

代码例子摘自:
https://doc.rust-lang.org/rust-by-example/custom_types/enum.html , 例子中分别定义了3中enum variants. 超级类似于empty struct、 tuple struct、 c like struct等, rust 枚举比c语言的 枚举而言拥有更强的封装和抽象能力,而非简单的分类!枚举对于rust而言极其重大,列如enum Option<T>和enum Result<T, E> , 所以我认为有必要啰嗦一下。

类型别名

#[derive(Debug)]
enum VeryVerboseEnumOfThingsToDoWithNumbers {
    Add,
    Subtract,
}

// 为上面的enum创建一个类型别名。
type Operations = VeryVerboseEnumOfThingsToDoWithNumbers;

impl VeryVerboseEnumOfThingsToDoWithNumbers {
    fn run(&self, x: i32, y: i32) -> i32 {
        //注意Self 也是上面enum的类型别名。
        match self {
            Self::Add => x + y,
            Self::Subtract => x - y,
        }
    }
}

fn main() {
  //我们可以通过它的别名来引用每个变体,而不是冗长和不方便的名字。
    let x = Operations::Add;
    let y = Operations::Subtract;
    println!("{:?}: {}",x,  x.run(3, 4));
    println!("{:?}: {}",y,  y.run(3, 4));
}

use 声明

#![allow(dead_code)]

enum Status {
    Rich,
    Poor,
}

enum Work {
    Civilian,
    Soldier,
}

fn main() {
    // Explicitly `use` each name so they are available without
    // manual scoping.
    //手动指定需要公开出来的变体。
    use crate::Status::{Poor, Rich};
    // Automatically `use` each name inside `Work`.
    //将enum Work下的变体自动都公开出来。
    use crate::Work::*;

    // Equivalent to `Status::Poor`.
    let status = Poor;
    // Equivalent to `Work::Civilian`.
    let work = Civilian;

    match status {
        // Note the lack of scoping because of the explicit `use` above.
        Rich => println!("The rich have lots of money!"),
        Poor => println!("The poor have no money..."),
    }

    match work {
        // Note again the lack of scoping.
        Civilian => println!("Civilians work!"),
        Soldier  => println!("Soldiers fight!"),
    }
}

C Like enum

#![allow(dead_code)]

//https://doc.rust-lang.org/nomicon/other-reprs.html
#[repr(C)]
enum Number {
    Zero, //默认从0开始。
    One = 22,
    Two,
}

// enum with explicit discriminator
#[repr(C)]
enum Color {
    Red = 0xff0000,
    Green = 0x00ff00,
    Blue = 0x0000ff,
}

fn main() {
    // `enums` can be cast as integers.
    println!("zero is {}", Number::Zero as i32);
    println!("one is {}", Number::One as i32);
    println!("one is {}", Number::Two as i32);

    println!("roses are #{:06x}", Color::Red as i32);
    println!("violets are #{:06x}", Color::Blue as i32);
}

repr(C)极其重大。它的目的相当简单明确:做C所做的事情。字段的顺序、大小和对齐方式正是C或C++所期望的。你希望跨越FFI边界的任何类型都应该有repr(C),由于C是编程界的通用语言。这对于更好地使用数据布局(如将值重新解释为不同类型)进行更精细的控制技巧也是必要的。
我们强烈提议您使用rust bindgen和/或cbindgen来管理您的FFI边界。Rust团队与这些项目密切合作,以确保它们工作可靠,并与当前和未来有关类型布局和reprs保证兼容。
由于repr(C)具有“用于FFI”和“用于布局控制”的双重用途,因此它可以应用于那些如果跨越FFI边界将是无意义的或有问题的类型。

enum variants模式匹配

if let 、 while let 、 match等等。

rust enum替代bool

bool只可以表达二态, 而enum不仅可以表达二态而且可以表达多态,而且令代码更具可读性,并且更易理解!rust编译器对于enum的检查也更加严格,列如未覆盖所有enum variants , 详情请参考原文:
http://blakesmith.me/2019/05/07/rust-patterns-enums-instead-of-booleans.html , 最重大的是:rust enum不仅可以表达多种状态(事件/类型等),而且每个状态可以封装携带数据,再配合上泛型、impl、trait 等rust语言特性! 所以使得rust enum的抽象表达能力更强!

一个自定义枚举类型是Copy语义还是Move语义?

在Rust语言中,所有类型的默认语义就是Move语义(移动语义即转移所有权语义),但是只有满足必定约束规则的类型才可以标记为Copy语义(复制语义), 即实现Copy trait。

Rust规定:对于一个自定义类型,只有其所有子成员(field item)都实现了Copy trait,代表这些子成员都是“复制语义”的,那么这个自定义类型整体才可以实现Copy trait, 即对外表明自己的Copy语义。

常见的数字类型、字符、bool类型、共享借用&(只读借用)等都是Rust语言默认就实现了Copy trait的类型,即都是Copy语义;而Box、Vec、可变借用&mut等都是移动语义,不具备Copy语义。

对于数组类型(固定长度数组)、tuple元组类型等,只有他内部的每一个元素的类型都是Copy语义的,那么这个数组类型和tuple元组类型整体就是Copy语义的,否则就是Move语义的。

对于自定义struct结构体和enum枚举类型而言,他们不会默认自动实现Copy trait,只有当他们内部的每一个子成员都是Copy语义的类型时,Rust编译器才允许我们为结构体和枚举体类型整体实现Copy trait, 对外表明他时Copy语义的。

(1) Copy语义数组:

fn main(){
    let mut a:[i32;5] = [1,2,3,4,5];
    print_x(a);
    println!("a:{:?}", a);
}

fn print_x( mut x:[i32;5]){

    println!("x:{:?}", x);
    x[0] = 99;
}

(2) Move语义数组

注意下面代码编译失败,注意看错误提示信息,明确告知我们固定长度数组[String;2] 是Move语义的,而非Copy语义的!!! 由此可证:一个固定长度的数组array,其到底是Move语义的,还是Copy语义的, 完全由其元素类型来决定!列如上面的代码中,其数组元素是数字类型i32,就是一个Copy语义的元素类型,所以整个数组就是Copy语义的;而下面代码中的数组元素类型是String,其是Move语义的, 所有整个数组就是Move语义的!



fn main(){
    let mut a:[String;2] = ["hello".to_string(), "world".to_string()];
    print_x(a);
    println!("a:{:?}", a);
}


fn print_x( mut x:[String;2]){

    println!("x:{:?}", x);
    x[0] = "hey".to_string();
}

Reference

https://rust-lang.github.io/unsafe-code-guidelines/layout/enums.html
http://www.rust-compare.com/site/enums.html
https://doc.rust-lang.org/rust-by-example/custom_types/enum.html
http://blakesmith.me/2019/05/07/rust-patterns-enums-instead-of-booleans.html
https://doc.rust-lang.org/nomicon/other-reprs.html
https://doc.rust-lang.org/reference/items/enumerations.html

《深入浅出Rust》 范长春著 机械工业出版社

后记:

我是一个普通的c++老码农,Rust语言爱好者,如今已四十不惑,青丝白发生,人谈不上机智,然多年来敏而好学,不敢懈怠, 随处学随处记,笔耕不辍,坚信好脑瓜不如烂笔头!如今赋闲家中,翻腾出来这些粗鄙之文,分享出来,抛砖引玉!不过本人水平有限,许多时候也是不求甚解,匆促行文,故而文中难免存有谬误,望诸君海涵指正!

© 版权声明

相关文章

1 条评论

  • 头像
    黑龙江高速交警 投稿者

    收藏了,感谢分享

    无记录
    回复