[英]How can I create enums with constant values in Rust?
我可以做這個:
enum MyEnum {
A(i32),
B(i32),
}
但不是這個:
enum MyEnum {
A(123), // 123 is a constant
B(456), // 456 is a constant
}
我可以使用單個字段為A
和B
創建結構,然后實現該字段,但我認為可能有更簡單的方法。 有沒有?
回答這個問題的最佳方法是弄清楚為什么要在枚舉中使用常量:您是將一個值與每個變體相關聯,還是希望每個變體成為該值(如 C 或 C++ 中的enum
)?
對於第一種情況,只留下沒有數據的枚舉變體並創建一個函數可能更有意義:
enum MyEnum {
A,
B,
}
impl MyEnum {
fn value(&self) -> i32 {
match *self {
MyEnum::A => 123,
MyEnum::B => 456,
}
}
}
// call like some_myenum_value.value()
這種方法可以多次應用,將許多單獨的信息與每個變體相關聯,例如,也許你也想要一個.name() -> &'static str
方法。 將來,這些函數甚至可以標記為const
函數。
對於第二種情況,您可以指定顯式整數標記值,就像 C/C++ 一樣:
enum MyEnum {
A = 123,
B = 456,
}
這可以以所有相同的方式matched
,但也可以轉換為整數MyEnum::A as i32
。 (請注意,像MyEnum::A | MyEnum::B
這樣的計算在 Rust 中並不是自動合法的:枚舉具有特定的值,它們不是位標志。)
創建具有常量值的“枚舉”,可以使用 structs 和關聯的常量來擴充。 這類似於像bitflags這樣的 crate 的工作方式以及它會生成什么。
此外,為了防止直接實例化MyEnum
,您可以使用#[non_exhaustive]
對其進行標記。
#[non_exhaustive]
struct MyEnum;
impl MyEnum {
pub const A: i32 = 123;
pub const B: i32 = 456;
}
然后,您只需通過訪問MyEnum::A
和MyEnum::B
就可以像其他方式一樣使用“枚舉”。
看到這個的人可能會FromPrimitive
的引入和棄用。 一個可能在這里也有用的替代品是enum_primitive 。 它允許您使用類似 C 的枚舉並讓它們在數字和邏輯表示之間進行轉換:
#[macro_use]
extern crate enum_primitive;
extern crate num;
use num::FromPrimitive;
enum_from_primitive! {
#[derive(Debug, PartialEq)]
enum FooBar {
Foo = 17,
Bar = 42,
Baz,
}
}
fn main() {
assert_eq!(FooBar::from_i32(17), Some(FooBar::Foo));
assert_eq!(FooBar::from_i32(42), Some(FooBar::Bar));
assert_eq!(FooBar::from_i32(43), Some(FooBar::Baz));
assert_eq!(FooBar::from_i32(91), None);
}
enum-map crate 提供了為枚舉記錄分配值的能力。 更重要的是,您可以將此宏與不同的值類型一起使用。
use enum_map::{enum_map, Enum}; // 0.6.2
#[derive(Debug, Enum)]
enum Example {
A,
B,
C,
}
fn main() {
let mut map = enum_map! {
Example::A => 1,
Example::B => 2,
Example::C => 3,
};
map[Example::C] = 4;
assert_eq!(map[Example::A], 1);
for (key, &value) in &map {
println!("{:?} has {} as value.", key, value);
}
}
這個怎么樣?
enum MyEnum {
A = 123,
B = 456,
}
assert_eq!(MyEnum::A as i32, 123i32);
assert_eq!(MyEnum::B as i32, 456i32);
只是提供另一個想法。
#[allow(non_snake_case, non_upper_case_globals)]
mod MyEnum {
pub const A: i32 = 123;
pub const B: i32 = 456;
}
然后,您可以通過訪問MyEnum::A
和MyEnum::B
或use MyEnum::*
來簡單地使用它。
這樣做相對於關聯常量的好處是您甚至可以嵌套更多枚舉。
#[allow(non_snake_case, non_upper_case_globals)]
mod MyEnum {
pub const A: i32 = 123;
pub const B: i32 = 456;
#[allow(non_snake_case, non_upper_case_globals)]
mod SubEnum {
pub const C: i32 = 789;
}
}
對於我的項目,我編寫了一個自動生成索引並設置初始值的宏。
#[macro_export]
macro_rules! cnum {
(@step $_idx:expr,) => {};
(@step $idx:expr, $head:ident, $($tail:ident,)*) => {
pub const $head: usize = $idx;
cnum!(@step $idx + 1usize, $($tail,)*);
};
($name:ident; $($n:ident),* $(,)* $({ $($i:item)* })?) => {
cnum!($name; 0usize; $($n),* $({ $($i)* })?);
};
($name:ident; $start:expr; $($n:ident),* $(,)* $({ $($i:item)* })?) => {
#[macro_use]
#[allow(dead_code, non_snake_case, non_upper_case_globals)]
pub mod $name {
use crate::cnum;
$($($i)*)?
cnum!(@step $start, $($n,)*);
}
};
}
然后你可以像這樣使用它,
cnum! { Tokens;
EOF,
WhiteSpace,
Identifier,
{
cnum! { Literal; 100;
Numeric,
String,
True,
False,
Nil,
}
cnum! { Keyword; 200;
For,
If,
Return,
}
}
}
我為此創建了一個 crate枚舉。
使用我的箱子的例子:
use enumeration::prelude::*;
enumerate!(MyEnum(u8; i32)
A = 123
B = 456
);
pub fn main() {
assert_eq!(*MyEnum::A.value(), 123);
assert_eq!(*MyEnum::B.value(), 456);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.