How can I create enums with constant values in Rust? - rust

I can do this:
enum MyEnum {
A(i32),
B(i32),
}
but not this:
enum MyEnum {
A(123), // 123 is a constant
B(456), // 456 is a constant
}
I can create the structures for A and B with a single field and then implement that field, but I think there might be an easier way. Is there any?

The best way to answer this is working out why you want constants in an enum: are you associating a value with each variant, or do you want each variant to be that value (like an enum in C or C++)?
For the first case, it probably makes more sense to just leave the enum variants with no data, and make a function:
enum MyEnum {
A,
B,
}
impl MyEnum {
fn value(&self) -> i32 {
match *self {
MyEnum::A => 123,
MyEnum::B => 456,
}
}
}
// call like some_myenum_value.value()
This approach can be applied many times, to associate many separate pieces of information with each variant, e.g. maybe you want a .name() -> &'static str method too. In the future, these functions can even be marked as const functions.
For the second case, you can assign explicit integer tag values, just like C/C++:
enum MyEnum {
A = 123,
B = 456,
}
This can be matched on in all the same ways, but can also be cast to an integer MyEnum::A as i32. (Note that computations like MyEnum::A | MyEnum::B are not automatically legal in Rust: enums have specific values, they're not bit-flags.)

Creating an "enum" with constant values, can be augmented using structs and associated constants.
This is similar to how crates like bitflags works and what it would generate.
Additionally, to prevent direct instantiation of MyEnum you can tag it with #[non_exhaustive].
#[non_exhaustive]
struct MyEnum;
impl MyEnum {
pub const A: i32 = 123;
pub const B: i32 = 456;
}
Then you simply use the "enum" as you otherwise would, by accessing MyEnum::A and MyEnum::B.

People looking at this may stumble upon the introduction and deprecation of FromPrimitive. A possible replacement which might also be useful here is enum_primitive. It allows you to use C-like enums and have them cast between numeric and logical representation:
#[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);
}

The enum-map crate provides the ability to assign a value to the enum record. What is more, you can use this macro with different value types.
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);
}
}

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

Just to give another idea.
#[allow(non_snake_case, non_upper_case_globals)]
mod MyEnum {
pub const A: i32 = 123;
pub const B: i32 = 456;
}
Then you can simply use it by accessing MyEnum::A and MyEnum::B or use MyEnum::*.
The advantage of doing this over associated constants is that you can even nest more enums.
#[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;
}
}
For my project I wrote a macro that automatically generates indexes and sets initial values.
#[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,)*);
}
};
}
Then you can use it like this,
cnum! { Tokens;
EOF,
WhiteSpace,
Identifier,
{
cnum! { Literal; 100;
Numeric,
String,
True,
False,
Nil,
}
cnum! { Keyword; 200;
For,
If,
Return,
}
}
}

I have created a crate enumeration just for this.
Example using my 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);
}

Related

C equivalent x-macro defined enum in Rust

I have been a C/C++ developer for more years than I care to remember. I am now starting out in Rust. Based on reading the documentation, I believe it will be able to address most, if not all, of my needs directly. However, I want to make sure that I can understand the Rust equivalent syntax for the following C++ syntax (I skipped error checking code for simplicity)
#include <iostream>
#include <map>
#define MY_ENUM \
X(AA, 17) \
X(BB, 42)
enum my_enum {
#define X(name, val) name = val,
MY_ENUM
#undef X
};
std::string enum_to_str(my_enum x)
{
static std::map<int, std::string> lookup = {
#define X(name, val) {val, #name},
MY_ENUM
#undef X
};
return lookup.at(x);
}
my_enum str_to_enum(const std::string &x)
{
static std::map<std::string, my_enum> lookup = {
#define X(name, val) {#name, my_enum(val)},
MY_ENUM
#undef X
};
return lookup.at(x);
}
int main()
{
my_enum x = AA;
const std::string bb("BB");
std::cout << "x=" << x << " which is " << enum_to_str(x) << std::endl;
std::cout << bb << " is " << str_to_enum(bb) << std::endl;
}
I believe I can achieve the to_string() functionality using either the Display or Debug trait in Rust, like so.
#[derive(Debug)]
enum MyEnum {
AA = 17,
}
//use std::fmt;
//impl fmt::Display for MyEnum {
// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// write!(f, "{:?}", self)
// }
//}
fn main() {
let x = MyEnum::AA;
println!("x={:?}", x);
}
What would be a way to do the string_to_enum() in Rust?
In rust, the normal way to convert strings into other types is by using the FromStr trait. However, FromStr can, by default, not be derived.
I thought I could recommend you the derive_more crate. Using this crate you can derive more traits, as the name implies. Unfortunately it does not support enums at this moment. I think that would be a fun addition to the library.
I think you can solve this problem with macros, but the clearest, cleanest, and most idiomatic way is just implementing FromStr yourself. You can use a match statement, matching on each of the strings and producing an enum variant. Doing this, you can also change the format of the strings which produce certain variants. For example, your enum variants might have capitalized names, but you may not want the strings to be capitalized as well. You could also add aliasses this way. An example:
enum MyEnum {
A,
B
}
struct FromStrError;
impl FromStr for MyEnum {
type Err = FromStrError;
fn from_str(v: &str) -> Result<Self, Self::Err> {
match v {
"a" => Ok(Self::A),
"b" => Ok(Self::B),
_ => Err(FromStrError),
}
}
}
fn main() {
let parsed: MyEnum = "a".parse()?
}
I understand this may not be an answer to your exact question: You wanted something equialent to the X macro. I think this is the way you will want to implement this, but if you really prefer a macro solution please let me know. Your question feels to me a bit like an X-Y problem. "how can I do X in rust", while what you actually want may be Y. Anyway, let me know if I can help you further.
NOTE: the to_string functionality you definitely can achieve with derive_more! See here
EDIT: if you really want a macro solution, and I want to stress again that I don't think this is the right solution, you can do something like this:
// define a macro generating both FromStr and Display implementations
macro_rules! stringy_enum {
(enum $name: ident {
$($variant: ident = $value: literal),* $(,)?
}) => {
#[derive(Debug, PartialEq)] // for tests, no need if you don't want it
enum $name {
$($variant = $value),*
}
impl std::str::FromStr for $name {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
$(
stringify!($variant) => {Ok(Self::$variant)}
)*
_ => {
Err(format!("couldn't parse to {}", stringify!($name)))
}
}
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
$(
Self::$variant => {write!(f, stringify!($variant))}
)*
}
}
}
};
}
stringy_enum!(
enum MyEnum {
A = 3,
B = 4,
}
);
#[test]
fn example() {
// strings can be parsed into enums
assert_eq!(Ok(MyEnum::A), "A".parse());
// enums can be converted into strings
assert_eq!(MyEnum::A.to_string(), "A");
}
note that this solution does not accept custom derives on the enum (the macro doesn't parse those), it doesn't accept anything other than variant = name pairs, it does not accept a pub keyword or other visibility attribues (though that's easy to add), it does not accept any other custom attributes other than derive, it does not support variants with values, etc. This is just an example. Don't literally use it.

Can one specify serde's rename_all rule at runtime?

I have a data model that I would like to be deserialized from "camelCase" to the rust standard "snake_case" when reading from a source, X. But I'd like to leave it in "snake_case" when reading or writing to another source, Y.
For example, the following code,
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Data {
foo_bar: String,
hello_word: String,
}
can only be encoded and decoded in camel case. Even if I manually defined my Serialize and Deserialize implementations, I can't define multiple for the same struct. I could define a second struct that's a copy/paste of the other and then derive but that method would get tedious with multiple large structs. What I would really like to do is specify that rename_all attribute at run-time. But I'm not seeing any way to do that in serde's API.
I think the best way sigh is to just write out one struct Data_ per #[serde(rename_all = ...)], then write one additional struct Data that will be the in-memory representation (which won't be serializable, to remove ambiguity), then implement From in both directions for the Data_s and Data so that they're interconvertible.
Thankfully, we can use a macro so that we only have to specify the fields once. (It is incredibly disgusting nonetheless.)
This playground available here.
use serde::{Deserialize, Serialize}; // 1.0.130
use serde_json; // 1.0.69
macro_rules! interconvertible {
($T:ident <-> $U:ident, $($field_name:ident),*) => {
impl From<$T> for $U {
fn from(t: $T) -> Self {
let $T { $($field_name),* } = t;
Self { $($field_name),* }
}
}
impl From<$U> for $T {
fn from(u: $U) -> Self {
let $U { $($field_name),* } = u;
Self { $($field_name),* }
}
}
};
}
macro_rules! create_data_structs {
($($field_name:ident: $field_type:ty),* $(,)?) => {
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct DataX {
$($field_name: $field_type),*
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "snake_case")]
struct DataY {
$($field_name: $field_type),*
}
#[derive(Debug)]
struct Data {
$($field_name: $field_type),*
}
interconvertible!(DataX <-> Data, $($field_name),*);
interconvertible!(DataY <-> Data, $($field_name),*);
}
}
create_data_structs!(foo_bar: String, hello_world: String);
fn main() -> serde_json::Result<()> {
let x1: DataX = serde_json::from_str(r#"{"fooBar": "a", "helloWorld": "b"}"#)?;
let y1: DataY = serde_json::from_str(r#"{"foo_bar": "a", "hello_world": "b"}"#)?;
println!("{:?}, {:?}", x1, y1);
let x2: Data = x1.into();
let y2: Data = y1.into();
println!("{:?}, {:?}", x2, y2);
let x_string = serde_json::to_string(&DataX::from(x2))?;
let y_string = serde_json::to_string(&DataY::from(y2))?;
println!("{:?}, {:?}", x_string, y_string);
Ok(())
}
The output is:
DataX { foo_bar: "a", hello_world: "b" }, DataY { foo_bar: "a", hello_world: "b" }
[Data { foo_bar: "a", hello_world: "b" }, Data { foo_bar: "a", hello_world: "b" }]
"{\"fooBar\":\"a\",\"helloWorld\":\"b\"}", "{\"foo_bar\":\"a\",\"hello_world\":\"b\"}"
Since I'm only every decoding from source X I can utilize the #[serde(alias = ???)] macro. So my above use case would be
#[derive(Serialize, Deserialize)]
struct Data {
#[serde(alias="fooBar")]
foo_bar: String,
#[serde(alias="helloWorld")]
hello_word: String,
}
It's still a little tedious but better than an intermediate struct. It won't work though if I want to decode or encode to different cases.
(I'm not going to mark this as an answer because it's a work-around for my specific use case. If anyone has a more generic solution feel free to answer.)

How to attach possible_values to a struct with structopt?

clap allows you to provide list of accepted values using possible_values like this.
let mode_vals = ["fast", "slow"];
.possible_values(&mode_vals)
How to do this with structopt?
Since structopt 0.3, you can use any method from App and Arg directly:
const MODE_VALS: &[&str] = &["fast", "slow"];
#[derive(StructOpt, Debug)]
struct Opt {
/// The velocity mode
#[structopt(short, long, possible_values(MODE_VALS))]
mode: String,
}
https://github.com/TeXitoi/structopt/blob/master/CHANGELOG.md#raw-attributes-are-removed-198-by-sphynx
clap’s possible_values is exposed as a field option, as shown in this structopt example:
//! How to use `arg_enum!` with `StructOpt`.
use clap::arg_enum;
use structopt::StructOpt;
arg_enum! {
#[derive(Debug)]
enum Baz {
Foo,
Bar,
FooBar
}
}
#[derive(StructOpt, Debug)]
struct Opt {
/// Important argument.
#[structopt(possible_values = &Baz::variants(), case_insensitive = true)]
i: Baz,
}
fn main() {
let opt = Opt::from_args();
println!("{:?}", opt);
}
Notably, this is making use of case_insensitive as well, to allow any case of those variants to be accepted.
If you want more granular control, you could omit case_insensitive and instead implement the variants yourself:
use structopt::StructOpt;
#[derive(Debug)]
enum Baz {
Foo,
Bar,
FooBar
}
impl Baz {
fn variants() -> [&'static str; 3] {
["foo", "bar", "foo-bar"]
}
}
#[derive(StructOpt, Debug)]
struct Opt {
/// Important argument.
#[structopt(possible_values = &Baz::variants())]
i: Baz,
}
fn main() {
let opt = Opt::from_args();
println!("{:?}", opt);
}
Finally, you could also use a string array in the same manner.

What is this strange syntax where an enum variant is used as a function?

Below is the example given by the mod documentation of syn::parse.
enum Item {
Struct(ItemStruct),
Enum(ItemEnum),
}
struct ItemStruct {
struct_token: Token![struct],
ident: Ident,
brace_token: token::Brace,
fields: Punctuated<Field, Token![,]>,
}
impl Parse for Item {
fn parse(input: ParseStream) -> Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(Token![struct]) {
input.parse().map(Item::Struct) // <-- here
} else if lookahead.peek(Token![enum]) {
input.parse().map(Item::Enum) // <-- and here
} else {
Err(lookahead.error())
}
}
}
Is input.parse().map(Item::Struct) a valid normal Rust syntax (appears not as Item::Struct is not a function), or is it a kind of special syntax for proc_macro libs? If the latter is the case, is there a documentation of the proc_macro specific syntax rules?
This syntax is standard Rust syntax. You can use tuple struct or tuple struct-like enum variants as functions. See this small example:
enum Color {
Str(String),
Rgb(u8, u8, u8),
}
struct Foo(bool);
// Use as function pointers (type annotations not necessary)
let f: fn(String) -> Color = Color::Str;
let g: fn(u8, u8, u8) -> Color = Color::Rgb;
let h: fn(bool) -> Foo = Foo;
In the next example, those functions are directly passed to another function (like Option::map) (Playground):
// A function which takes a function
fn string_fn<O, F>(f: F) -> O
where
F: FnOnce(String) -> O,
{
f("peter".to_string())
}
string_fn(|s| println!("{}", s)); // using a clojure
string_fn(std::mem::drop); // using a function pointer
// Using the enum variant as function
let _: Color = string_fn(Color::Str);
You can find out more about this feature, in this chapter of the book.

Is there a way to create a copy of an enum with some field values updated?

I have an enum with several record variants:
enum A {
Var1 { a: i64, b: i64 },
Var2 { c: i32, d: i32 },
}
I want to create a modified copy of such an enum (with different behavior for each variant). I know I can do this:
match a {
A::Var1 { a, b } => A::Var1 { a: new_a, b },
A::Var2 { c, d } => A::Var2 { c, d: new_d },
}
However each variant has quite a few fields, and I'd prefer not to explicitly pass them all. Is there any way to say "clone this enum, except use this value for field x instead of the cloned value"?
Not exactly.
There's "functional record update syntax", but it's only for structs:
struct Foo {
bar: u8,
baz: u8,
quux: u8,
}
fn foo() {
let foobar = Foo {
bar: 1,
baz: 2,
quux: 3,
};
let bazquux = Foo { baz: 4, ..foobar };
}
Best you can do without creating structs for each variant is something like this:
let mut altered = x.clone();
match &mut altered {
A::Var1 { a, .. } => *a = new_a,
A::Var2 { d, .. } => *d = new_d,
};
altered
I'm afraid you're hitting one of the restrictions on Rust based on its design and your only real solution is mutation in-place and writing a mutator function or four.
The problem with enums is that you need to match to be able to do anything on them. Until that point, Rust knows or infers very little about what the struct actually is. An additional problem is the lack of any kind of reflection-like ability to allow for the ability to query a type and figure out if it has a field, and the inability to do anything but exhaustively match all contents.
Honestly, the cleanest way may actually depend on the purpose of your mutations. Are they a defined set of changes to an enum based on a business concern of some sort? If so, you may actually want to wrap your logic into a trait extension and use that to encapsulate the logic.
Consider, for instance, a very contrived example. We're building an application that has to deal with different items and apply taxes to them. Said taxes depend on the type of products, and for some reason, all our products are represented by variants of an enum, like so:
#[derive(Debug)]
enum Item {
Food { price: u8, calories: u8 },
Technology { price: u8 },
}
trait TaxExt {
fn apply_tax(&mut self);
}
impl TaxExt for Item {
fn apply_tax(&mut self) {
match self {
&mut Item::Food {
ref mut price,
calories: _,
} => {
// Our food costs double, for tax reasons
*price *= 2;
}
&mut Item::Technology { ref mut price } => {
// Technology has a 1 unit tax
*price += 1;
}
}
}
}
fn main() {
let mut obj = Item::Food {
price: 3,
calories: 200,
};
obj.apply_tax();
println!("{:?}", obj);
}
playground
Assuming you can split your logic like so, it is probably the cleanest way to structure this.

Resources