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语言爱好者,如今已四十不惑,青丝白发生,人谈不上机智,然多年来敏而好学,不敢懈怠, 随处学随处记,笔耕不辍,坚信好脑瓜不如烂笔头!如今赋闲家中,翻腾出来这些粗鄙之文,分享出来,抛砖引玉!不过本人水平有限,许多时候也是不求甚解,匆促行文,故而文中难免存有谬误,望诸君海涵指正!
收藏了,感谢分享