How to match an enum variant in a match - rust

Is there a position for # operator or some other solution that would bind the variable in a match arm to a variant type rather than to the entire enum? In the following example, all bar, baz and qux have types of Foo, rather than Foo::Bar, Foo::Baz, Foo::Qux and the sample fails to compile.
enum Foo {
Bar(i32),
Baz{s: String},
Qux,
}
fn main() {
let foo = Foo::Bar(42);
match foo {
bar # Bar(..) => bar.0.to_string(),
baz # Baz{..} => baz.s,
qux # Qux => "".to_owned(),
}
}

Though handy if they existed, there is no such thing as an enum variant type in Rust. All variants have the same type. Their contents, on the other hand, might carry values of different types.
For example, both Foo::Bar(1) and Foo::Qux have the same type, which is Foo. So you can't bind those values and treat them differently as they are of the same type.
The most “idiomatic” solution I can think of is to just grab what's inside of the specific enum variant you're currently matching on, like so:
fn main() {
let foo = Foo::Bar(42);
let my_string = match foo {
Bar(bar) => bar.to_string(),
Baz{ s } => s,
Qux => "".to_owned(),
};
}

To reuse a variant enum you need to use enum tuple style and define external structure, it's a lot more used than enum struct:
struct Bar(i32);
struct Baz {
s: String,
}
enum Foo {
Bar(Bar),
Baz(Baz),
Qux,
}
fn main() {
let foo = Foo::Bar(Bar(42));
let foo = match foo {
Foo::Bar(bar) => bar.0.to_string(),
Foo::Baz(baz) => baz.s,
Foo::Qux => "".to_owned(),
};
assert_eq!(foo, "42");
}

I think the syntax you're looking for is this?
enum Foo {
Bar(i32),
Baz{s: String},
Qux,
}
fn main() {
let foo = Foo::Bar(42);
let s = match foo {
Foo::Bar(bar) => bar.to_string(),
Foo::Baz{s} => s,
Foo::Qux => "".to_owned(),
};
dbg!(s);
}

What you're looking for is use:
enum Foo {
Bar(i32),
Baz{s: String},
Qux,
}
fn main() {
use Foo::*;
let foo = Bar(42);
let s = match foo {
Bar(bar) => bar.to_string(),
Baz{s} => s,
Qux => "".to_owned(),
};
}

Related

How to define functions for specific variants

I have an enum with an associated show function.
enum Foo {
Bar(u32),
Baz
}
fn show(foo: Foo) {
match foo {
Foo::Bar(_) => show_bar(foo),
Foo::Baz => show_baz(foo),
}
}
Based on the variant, a specialized show_* function is called.
fn show_bar(foo: Foo) {
match foo {
Foo::Bar(x) => println!("BAR({x})"),
_ => (),
}
}
fn show_baz(foo: Foo) {
match foo {
Foo::Baz => println!("BAZ"),
_ => (),
}
}
Since I can't pass variants as types, I need to repeat the match foo pattern matching inside each show_* function, which is a bit verbose and undeeded.
Is there a better / more idiomatic way to do this?
I'm a fairly simple soul and not a Rust expert, so maybe this doesn't meet your needs for some reason - but why not simply pass the associated data? (Which is nothing in the case of Baz)
enum Foo {
Bar(u32),
Baz
}
fn show(foo: Foo) {
match foo {
Foo::Bar(barValue) => show_bar(barValue),
Foo::Baz => show_baz(),
}
}
fn show_bar(x: u32) {
println!("BAR({x})"),
}
fn show_baz() {
println!("BAZ"),
}

Pattern match on enum type don't care about arguments

enum Foo {
Bar(usize, usize),
Baz(isize),
}
impl Operator {
fn value(&self) -> usize {
use Foo::*;
match (self) {
Bar(_) => 1,
Baz(_) => 2,
}
}
}
neither Bar(_), Bar, nor Bar() work.
I just want to pattern-match the enum type, and the arguments don't matter at all. I would prefer to not have to remember how many _ I need to put for each enum variant, and not have to change these patterns in the case that the enum definition changes.
Since Bar has two arguments you either have to match them both:
match self {
Bar(_, _) => 1,
Baz(_) => 2,
}
or use ..
match self {
Bar(..)
Baz(_) => 2,
}

Want to pass a parameterized enum to a function using _ as parameter (like my_fun(MyEnum::Type(_)))

I have this next_expected_kind method that return the next item of an Iterable<Kind> if it is the expected type, or an error if not.
It works fine for non parameterized types like Kind1, but I don't know how to use it if the type that needs parameters like Kind2.
Something like:
let _val = match s.next_expected_kind(Kind::Kind2(str)) {
Ok(k) => str,
_ => panic!("error"),
};
Is there any tricky to make it?
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d21d5cff42fcca633e95b4915ce2bf1d
#[derive(PartialEq, Eq)]
enum Kind {
Kind1,
Kind2(String),
}
struct S {
kinds: std::vec::IntoIter<Kind>,
}
impl S {
fn next_expected_kind(&mut self, expected: Kind) -> Result<Kind, &str> {
match self.kinds.next() {
Some(k) if k == expected => Ok(k),
_ => Err("not expected"),
}
}
}
fn main() {
let mut s = S {
kinds: vec![Kind::Kind1, Kind::Kind2(String::from("2"))].into_iter(),
};
_ = s.next_expected_kind(Kind::Kind1);
// let _val = s.next_expected_kind(Kind::Kind2(str));
let _val = match s.kinds.next() {
Some(Kind::Kind2(str)) => str,
_ => panic!("not expected"),
};
}
You could use std::mem::discriminant() like this:
use std::mem::{Discriminant, discriminant};
#[derive(Debug, PartialEq, Eq)]
enum Kind {
Kind1,
Kind2(String),
}
struct S {
kinds: std::vec::IntoIter<Kind>,
}
impl S {
fn next_expected_kind(&mut self, expected: Discriminant<Kind>) -> Result<Kind, &str> {
match self.kinds.next() {
Some(k) if discriminant(&k) == expected => Ok(k),
_ => Err("not expected"),
}
}
}
fn main() {
let mut s = S {
kinds: vec![Kind::Kind1, Kind::Kind2(String::from("2"))].into_iter(),
};
_ = dbg!(s.next_expected_kind(discriminant(&Kind::Kind1)));
let _val = dbg!(s.next_expected_kind(discriminant(&Kind::Kind2(String::new()))));
}
The obvious drawback being that you'll have to create an instance with "empty" or default data wherever you want to call it.
The only other way I can think of would be to write a macro since you can't pass just the "variant" of an enum around.
#[derive(Debug, PartialEq, Eq)]
enum Kind {
Kind1(String),
Kind2(i32, i32),
}
struct S {
kinds: std::vec::IntoIter<Kind>,
}
macro_rules! next_expected_kind {
($self:expr, $expected:path) => {
match $self.kinds.next() {
Some(k) if matches!(k, $expected(..)) => Ok(k),
_ => Err("not expected"),
}
}
}
fn main() {
let mut s = S {
kinds: vec![Kind::Kind1(String::from("1")), Kind::Kind2(2,3)].into_iter(),
};
_ = dbg!(next_expected_kind!(&mut s, Kind::Kind1));
let _val = dbg!(next_expected_kind!(&mut s, Kind::Kind2));
}
Note: this has the limitation that all variants have to be tuple variants or struct variants and it's a bit clunky to use.

Is there a way to match on all variants of an enum in rust?

Is there something that is equivalent to this:
enum ABC {
A(u32),
B(i32),
C(f64),
}
fn main() {
let abc = ABC::A(42);
match abc {
_(foo) => foo,
}
}
The reason why I'm asking is that sometimes, I want to use an enum for the different possible types, but most cases I need to handle the data in the enum variants the same exact way.
If you want to match against all possible variants and perform a common action you can
write a macro for it and reduce code repetition:
enum ABC {
A(u32),
B(i32),
C(f64),
}
#[macro_use]
macro_rules! per_letter {
($val:expr, $pattern:pat => { $res:expr }) => (
match $val {
$crate::ABC::A($pattern) => $res,
$crate::ABC::B($pattern) => $res,
$crate::ABC::C($pattern) => $res,
}
)
}
impl ABC {
pub fn print_inner(&self) {
per_letter!(self, letter => {println!("{}", letter)});
}
pub fn print_quad(&self) {
per_letter!(self, letter => {println!("{}", *letter**letter)});
}
}
Note that this is limited. You can't have multiple possible return types without wrapping them in an enum. But it's still useful if your inner types share a common functionality.
fn main() {
let foo = ABC::A(2);
foo.print_inner();
foo.print_quad();
}

How can I create enums with constant values in 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);
}

Resources