提示
所有帖子都会不定期添加补充实战过程中发现的新内容,力争一篇文章涵盖所有内容,避免你多次查找文章,所以最好点赞或收藏,时不时回来看看。
rust不一样的枚举
对于C类和Pascal类语言的枚举,都很单纯的表示一种单一的数据类型。但rust的枚举却不同,它可以像结构体一样使用,所以称为枚举体。
传统类C方式使用枚举体
rust中的枚举体也可以像传统枚举那样使用;
pub enum Number {
One = 1,
Two = 2,
Four = 4,
}
fn main() {
let a: u32 = Number::One as u32;
println!("{}", a),
}
这样的使用方式,不能自定义成员的具体数据类型,比如上面的枚举有三个值,每个值小于255,用无符号8位就可以表示,但是rust的枚举体以上面方式使用时并不能指定成员的数据类型,使用时需要as为目标对象的类型,虽然麻烦一点点,但至少提供了传统的软件代码用rust重写时的直译支持;如果想指定枚举值的数据类型,可以参考我下面的写法
#[repr(u8)] // 指定为U8类型
pub enum PenStyle {
Solid = 0,
Dash = 1,
Dot = 2,
}
或者使用下面的方法也可以:
enum PenStyle {}
impl PenStyle {
const SOLID: u8 = 0;
const DASH: u8 = 1;
const DOT: u8 = 2;
}
使用时也很方便,直接match
match self.style {
PenStyle ::SOLID => { },
PenStyle ::DASH=> { },
_ => { },
}
接下来就是rus枚举体特有的使用方法了。
无参数枚举体
#[derive(PartialEq)]
pub enum Number {
One,
Two,
Four,
}
fn main() {
let a = Number::One;
match a {
Number::One => println!("1"),
Number::Two => println!("2"),
Number::Four => println!("4"),
}
// 比较一下
if a == Number::One {
println!("{}", "a为One");
}
}
上面的枚举体定义了三个值(并不是三个类型), 这是枚举体最常见的使用方式。match是表示判断a和哪个枚举值匹配,#[derive(PartialEq)]保障了枚举体可以用if a == Number::One这样的判断是否相等。
带参数枚举体
带参数的枚举体是rust特有的,对于这样一种新的枚举,我们可能都不知道在哪里或什么情况下用,我以windows下消息响应举例说明一下,在传统的桌面开发语言中,如果要自己响应windows窗体消息,需要在wndproc里判断消息的类型,消息类型是个整形值,如WM_QUIT(整形值18)、WM_MOUSEMOVE(整形值512)、WM_KEYDOWN(整形值256),传统开发时,根据消息值判断出来是什么消息,然后再去消息的数据结构里取对应消息的数据,如果鼠标坐标,按键键值等,如果用rust枚举体实现的话,就可以直接把消息定义成对应的消息结构
enum Message {
Quit,
MouseMove { x: i32, y: i32 },
KeyDown(String),
}
接下来,给枚举体定义个调用方法Call,对你没听错,枚举体里可以实现方法,这可能也是为什么叫枚举体的原因吧,像结构体一样,完整代码如下:
fn main() {
enum Message {
Quit,
MouseMove { x: i32, y: i32 },
KeyDown(String),
}
impl Message {
fn call(&self) { // 注意self并不是枚举本身,而是被调用枚举的其中一个实例
match &*self { // 因为KeyDown用到了String,所以这里要加&借用,否则可以去掉&
Message::Quit => println!("{}", "Message Quit"),
Message::MouseMove {x, y} => println!("Message MouseMove x={}, y={}", x, y),
Message::KeyDown(s) => println!("Message KeyDown {}", s),
}
}
}
let m = Message::KeyDown(String::from("R"));
m.call();
}
多的不说,多看上面的代码体会吧。
枚举和集合
在Delphi里枚举是可以用集合来包含的,一个集合可以包含0…n个枚举(当然Delphi里的集合和rust里的集合并不是同一个意思),例如文本的样式有加粗、倾斜、下划线等,文本样式集合可以包含其中的0…n个,Delphi里定义如下
type
THCFontStyle = (tsBold, tsItalic, tsUnderline, tsStrikeOut, tsSuperscript, tsSubscript); // 枚举值
THCFontStyles = set of THCFontStyle; // 集合
使用时可以判断枚举是否在集合里
if tsBold in FFontStyles then
…
在rust里也可以实现上面的功能,而且更加的优秀,不过rust里并不是使用的枚举体来实现,我写在这里,是为了方便有其他语言经验的朋友在实现类似这样的集合时查枚举相关的资料可以看到,
在rust里实现上述功能需要用到bitflags宏,使用bitflags宏中要在Cargo.toml中的[dependencies]下增加 bitflags,
[dependencies]
bitflags = “1.2.1” // 自己决定是否使用最新版本或选择合适的版本
然后在lib.rs或main.rs中最靠前的位置添加
#[macro_use]
extern crate bitflags; // 这一行留心一下,下面可能会说到
少整没用的,直接上代码
bitflags! {
pub struct FontStyles: u8 {
const BOLD = 0b00000001;
const ITALIC = 0b00000010;
const UNDERLINE = 0b00000100;
const STRIKEOUT = 0b00001000;
const SUPERSCRIPT = 0b00010000;
const SUBSCRIPT = 0b00100000;
}
}
struct WinCanvas {
font_styles: FontStyles,
}
fn main() {
let mut canvas = WinCanvas {
font_styles: FontStyles::empty(),
};
canvas.font_styles |= FontStyles::BOLD;
if canvas.font_styles.contains(FontStyles::BOLD) {
println!("加粗");
}
}
因为我的枚举值少于8个,所以限定了是u8类型,另外bitflags还支持很多方法,如all(),Insert()等,具体可以看bitflags的源码单元(什么?不知道怎么找源码单元?如果你用vscode的话,直接在上面让你留心的那行中的bitflags上按下ctrl鼠标左键单击就会跳转到源码单元了)。
写博客真耗时间。
参考
我参考了下面的,你也可以看看。
https://docs.rs/enumn/0.1.3/enumn/#signature
https://docs.rs/bitflags/1.2.1/bitflags/index.html
http://www.voidcn.com/article/p-rfecwfxz-bug.html
https://segmentfault.com/a/1190000023576996
https://github.com/yujinliang/rust_learn/blob/master/bit_operations/bitflags/src/main.rs