【问题标题】:How can I create enums with constant values in Rust?如何在 Rust 中创建具有常量值的枚举?
【发布时间】:2016-08-24 00:55:10
【问题描述】:

我可以这样做:

enum MyEnum {
    A(i32),
    B(i32),
}

但不是这个:

enum MyEnum {
    A(123), // 123 is a constant
    B(456), // 456 is a constant
}

我可以使用单个字段为AB 创建结构,然后实现该字段,但我认为可能有更简单的方法。有吗?

【问题讨论】:

    标签: rust


    【解决方案1】:

    只是提供另一个想法。

    #[allow(non_snake_case, non_upper_case_globals)]
    mod MyEnum {
        pub const A: i32 = 123;
        pub const B: i32 = 456;
    }
    

    然后您可以通过访问MyEnum::AMyEnum::Buse MyEnum::* 来简单地使用它。

    associated constants 相比,这样做的好处是您甚至可以嵌套更多枚举。

    #[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_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,)+) => {
            cnum!($name; 0usize; $($n),+);
        };
        ($name: ident; $($n:ident),*) => {
            cnum!($name; 0usize; $($n),*);
        };
        ($name: ident; $start: expr; $($n:ident,)+) => {
            cnum!($name; $start; $($n),+);
        };
        ($name: ident; $start: expr; $($n:ident),*) => {
            #[allow(non_snake_case, non_upper_case_globals)]
            pub mod $name {
                cnum!(@step $start, $($n,)*);
            }
        };
        // nested
        ($name: ident; $($n:ident,)+ { $($i:item)* }) => {
            cnum!($name; 0usize; $($n),+ { $($i)* });
        };
        ($name: ident; $($n:ident),* { $($i:item)* }) => {
            cnum!($name; 0usize; $($n),* { $($i)* });
        };
        ($name: ident; $start: expr; $($n:ident,)+ { $($i:item)* }) => {
            cnum!($name; $start; $($n),+ { $($i)* });
        };
        ($name: ident; $start: expr; $($n:ident),* { $($i:item)* }) => {
            #[allow(non_snake_case, non_upper_case_globals)]
            pub mod $name {
                $($i)*
                cnum!(@step $start, $($n,)*);
            }
        };
    }
    

    那你就可以这样使用了,

    cnum! { Tokens;
        EOF,
        WhiteSpace,
        Identifier,
        {
            cnum! { Literal; 100;
                Numeric,
                String,
                True,
                False,
                Nil,
            }
    
            cnum! { Keyword; 200;
                For,
                If,
                Return,
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      这个怎么样?

      enum MyEnum {
          A = 123,
          B = 456,
      }
      
      assert_eq!(MyEnum::A as i32, 123i32);
      assert_eq!(MyEnum::B as i32, 456i32);
      

      【讨论】:

        【解决方案3】:

        创建具有常量值的“枚举”,可以使用结构和associated constants 进行扩充。 这类似于像 bitflags 这样的 crate 的工作方式以及它会生成什么。

        此外,为了防止直接实例化MyEnum,您可以使用#[non_exhaustive] 对其进行标记。

        #[non_exhaustive]
        struct MyEnum;
        
        impl MyEnum {
            pub const A: i32 = 123;
            pub const B: i32 = 456;
        }
        

        然后您只需访问MyEnum::AMyEnum::B,就可以像其他方式一样使用“枚举”。

        【讨论】:

          【解决方案4】:

          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);
              }
          }
          

          【讨论】:

            【解决方案5】:

            回答这个问题的最佳方法是弄清楚为什么要在枚举中使用常量:您是要将值与每个变体相关联,还是希望每个变体成为该值(如 @ 987654321@ 在 C 或 C++ 中)?

            对于第一种情况,只留下没有数据的枚举变体并创建一个函数可能更有意义:

            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 on,但也可以转换为整数MyEnum::A as i32。 (请注意,像 MyEnum::A | MyEnum::B 这样的计算在 Rust 中并不自动合法:枚举具有特定的值,它们不是位标志。)

            【讨论】:

            • 如何匹配值(在value函数中实现)?
            • @MatejKormuth 这已经晚了,但是,您只需匹配方法的返回值。假设您的枚举值存储在名为some_myenum_value 的变量中,只需执行match some_myenum_value.value() { ...
            【解决方案6】:

            查看此内容的人可能会偶然发现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);
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-11-21
              • 2014-07-28
              • 2021-04-11
              • 2020-05-06
              • 2014-09-26
              相关资源
              最近更新 更多