Can I limit a method to just _some_ enum values rather than to any?

Here's an enum with 3 kinds of value.
What I want to do is have a method that only works on two of the kinds but I don't know how -- or if -- I can do what I want.
See the commented out code.

#[derive(Clone, Debug)]
pub enum Value {
    Int(i64),
    Str(String),
    Real(f64),
}

impl Value {
    fn process(&self, v: Value) {
        println!("processed {:?} with {:?}", self, v)
    }
    
    /*
    fn special<T: Int | Real>(&self, v: T) {
        println!("special {:?} with {:?}", self, v)
    }
    */
}


fn main() {
   let v1 = Value::Int(5);
   let v2 = Value::Str("one".to_string());
   v1.process(v2);
   let v3 = Value::Real(9.2);
   // v3.special(v1);
}

(Playground)

No, unfortunately there's no such feature.

The best you can do is redefine enum:

pub enum Value {
    Numeric(NumericValue),
    Str(String),
}

pub enum NumericValue {
    Int(i64),
    Real(f64),
}

OK, thank you!

Expanding on @kornek's point... If I want to only accept a subset of some value, I will typically provide some sort of "view" which encodes those constraints in the type system and the operation will be defined on this view instead.

By returning an Option, the type system will also force people to make sure the invariant is upheld (i.e. special() only accepts i64 and f64).

enum Value {
    Int(i64),
    Str(String),
    Real(f64),
}

impl Value {
  fn as_numeric(&self) -> Option<NumericValue> {
    match self {
      Value::Int(i) => Some(NumericValue::Int(i)),
      Value::Float(f) => Some(NumericValue::Float(f)),
      Value::Str(_) => None,
    }
  }
}

enum NumericValue {
  Int(i64),
  Real(f64),
}

impl NumericValue {
  fn special(self) { ... }
}

Thanks for that. It does let me do what I want, although less directly than I hoped.

Yeah, what you are probably looking for is something like "narrowing". For example, languages like TypeScript let you use checks on the value at runtime to affect what it infers your value's type to be.

type Value = number | string;

function special(v: number) { ... }

function main() {
  const x: Value = ...;

  // special(x)    ERROR: number|string is not assignable to number
  if (typeof x == "number") {
    special(x); // we know "x" can only be a number, now
  }
}

However, Rust's type system doesn't work that way.

An enum variant is part of its value and only known at runtime, and trait bounds are used to constrain code based entirely on information known at compile time. There is no way to alter/narrow a variable's type by doing checks on the runtime value.

While that does means some things are a bit more verbose, we gain a lot of performance and control because we know that the x in let x: u32 = 42 will be exactly 4 bytes. Whereas in general, the x in TypeScript would often be a pointer to a heap-allocated u32 alongside some type and GC information (ignoring JIT optimisations, which are never guaranteed).

Another option could be to not use an enum but a separate strict for each variant your enum currently has. Then you could implement a trait for just some of them and the function could bound the parameter type with the trait.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.