I have an enum that holds variants which contain certain types. I want to write a get
method that automatically returns the right type if the enum variant contains this type.
pub enum Var {
Switch(bool),
PositiveInteger(u32),
Integer(i32),
PositiveFloat(f64),
Float(f64),
Phase(f64), // a float x, where 0.0 <= x < 2Pi
Turn(f64), // the same, but with 0.0 <= x < 1.0
}
Somewhere else in the code I want to get the value
inside the variant e.g. PositiveFloat( value )
. But it doesn't seem possible (to me) to write a generic function, like this:
impl Var {
pub fn get<T>(&self) -> T
where
T: Default,
{
match self {
Var::Switch(value) => value,
Var::PositiveInteger(value) => value,
// etc
}
I instead seem to have to implement a different get-method for each variant of the enum: get_positive_integer()
, get_integer()
, etc.
Maybe I am looking about the problem the wrong way. What I want to achieve something like this:
let positive_integer = Var::PositiveInteger(1);
let x: u32 = positive_integer.get();
let float = Var::Float(0.1);
let y: f64 = float.get();
CodePudding user response:
You can create a trait for that:
pub trait VarTypes: Sized {
fn get(var: &Var) -> Option<Self>;
}
impl VarTypes for bool {
fn get(var: &Var) -> Option<Self> {
match *var {
Var::Switch(v) => Some(v),
_ => None,
}
}
}
impl VarTypes for u32 {
fn get(var: &Var) -> Option<Self> {
match *var {
Var::PositiveInteger(v) => Some(v),
_ => None,
}
}
}
impl VarTypes for i32 {
fn get(var: &Var) -> Option<Self> {
match *var {
Var::Integer(v) => Some(v),
_ => None,
}
}
}
impl VarTypes for f64 {
fn get(var: &Var) -> Option<Self> {
match *var {
Var::PositiveFloat(v) | Var::Float(v) | Var::Phase(v) | Var::Turn(v) => Some(v),
_ => None,
}
}
}
Then you can implement get()
with it:
impl Var {
pub fn get<T>(&self) -> T
where
T: VarTypes Default,
{
T::get(self).unwrap_or_default()
}
}
CodePudding user response:
You can't define the method in an impl Var
directly because you'd need a different implementation for every type. What you can do instead is define a generic trait and implement that for every type you want:
trait Get<T> {
fn get(&self) -> Option<T>;
}
impl Get<bool> for Var {
fn get(&self) -> Option<bool> {
match self {
Var::Switch(value) => Some(*value),
_ => None,
}
}
}
impl Get<u32> for Var {
fn get(&self) -> Option<u32> {
match self {
Var::PositiveInteger(v) => Some(*v),
_ => None,
}
}
}
impl Get<f64> for Var {
fn get(&self) -> Option<f64> {
match self {
Var::PositiveFloat(v) | Var::Float(v) | Var::Phase(v) | Var::Turn(v) => Some(*v),
_ => None,
}
}
}
// …
I changed the return type to Option<T>
because that relaxes the requirement on T
and is more in line like other similar methods (slice::get
, ...) and you can regain your semantics with a simple unwrap_or_default()
:
let positive_integer = Var::PositiveInteger(1);
let x: u32 = positive_integer.get().unwrap_or_default();