Hope match can suppor multi enum cast

pub enum Value {
  Int8(i8),
  Int16(i16),
  Int32(i32),
}
match value {
  // in 2024-02-23 rust nightly version is error
  Value::Int8(v) | Value::Int16(v) | Value::Int32(v) => { do_something() }
}

but i think here the compiler can cast all value to i32. Does here any features or plan?

No, you can't match different types using the same match variable like that. You're going to have to write three separate match arms.

However, depending on the structure of your code, you might be able to write a From or TryFrom impl, or at least a conversion method, so that you only need to write that match once.

I see 2 features must be added to make this real:

  • Allow "local" bindings in complex expressions with patterns (first of all, match)
  • Add optional "in let" (or alternatively "where let") clause after MatchArmGuard for new binding

If we allow these 2 features, we could write next:

pub enum Value {
  Int8(i8),
  Int16(i16),
  Int32(i32),
}
match value {
    Value::Int8(local  v1) in let v = i32::from(v1) 
  | Value::Int16(local v2) in let v = i32::from(v2) 
  | Value::Int32(v)
        => { do_something() }
}

But is it unclear how is it appropriate for the rust community and how is this difficult to add into the language.

It seems to me that writing

do_something(match v {
    Value::Int8(v) => v.into(),
    Value::Int16(v) => v.into(),
    Value::Int32(v) => v.into(),
})

is not worse than writing

match value {
    Value::Int8(local  v1) in let v = i32::from(v1) 
  | Value::Int16(local v2) in let v = i32::from(v2) 
  | Value::Int32(v)
        => { do_something(v) }
}

It would be tedious to have to write that match expression in a bunch of places... which is where @josh's suggestions come in. Concretely, this compiles now:

fn do_something(x: i32) { println!("got here: {x}"); }

pub enum Value {
  Int8(i8),
  Int16(i16),
  Int32(i32),
}

// explicit conversion with callback -- may be needed for
// more complicated inner structures
impl Value {
    pub fn apply<T, F: FnOnce(i32) -> T>(&self, f: F) -> T {
        match *self {
            Value::Int8(v) => f(v.into()),
            Value::Int16(v) => f(v.into()),
            Value::Int32(v) => f(v.into()),
        }
    }
}

// enable into() -- better for anything that's Copy
impl std::convert::From<Value> for i32 {
    fn from(v: Value) -> i32 {
        match v {
            Value::Int8(v) => v.into(),
            Value::Int16(v) => v.into(),
            Value::Int32(v) => v.into(),
        }
    }
}

pub fn do_something_with_value_1(v: Value) {
    v.apply(do_something)
}

pub fn do_something_with_value_2(v: Value) {
    do_something(v.into())
}
2 Likes

There's a proc macro that makes such code less tedious to maintain:

3 Likes
impl Value {
    fn to_i32(self) -> i32 {
        Int8(v) => v.into(),
        Int16(v) => v.into(),
        Int32(v) => v.into(),
    }
}

then just use .to_i32() instead of match elsewhere :upside_down_face:

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.