Search code examples
genericsrustenumstraits

Implementing Generic Trait for Enum in Rust


Is there a way to implement a Trait with a generic type T for an enum? something like this:

use rand::thread_rng;
use rand::seq::SliceRandom;

pub enum Color {
    Red,
    Blue,
    Green,
}

trait Shuffable {
    fn values<T>() -> Vec<T>;

    fn shuffled<T: Shuffable>() -> Vec<T> {
        let mut items = T::values();
        items.shuffle(&mut thread_rng());

        items
    }
}

impl Shuffable for Color {
    fn values<Color>() -> Vec<Color> {
        vec![Color::Red, Color::Blue, Color::Green]
    }
}

This errors on not being able to relate Enum items to the Type itself

error[E0599]: no associated item named `Red` found for type parameter `Color` in the current scope
  --> src/game.rs:25:21
   |
25 |         vec![Color::Red, Color::Blue, Color::Green]
   |                     ^^^ associated item not found in `Color`

error[E0599]: no associated item named `Blue` found for type parameter `Color` in the current scope
  --> src/game.rs:25:33
   |
25 |         vec![Color::Red, Color::Blue, Color::Green]
   |                                 ^^^^ associated item not found in `Color`

error[E0599]: no associated item named `Green` found for type parameter `Color` in the current scope
  --> src/game.rs:25:46
   |
25 |         vec![Color::Red, Color::Blue, Color::Green]
   |                                              ^^^^^ associated item not found in `Color`

I have checked https://github.com/rust-lang/rust/issues/52118 but it doesn't seem to be related!


Solution

  • As a starting point, we can make your code work as follows:

    use rand::thread_rng;
    use rand::seq::SliceRandom;
    
    #[derive(Debug)]
    pub enum Color {
        Red,
        Blue,
        Green,
    }
    
    trait Shuffable where Self: Sized {
        fn values() -> Vec<Self>;
        fn shuffled() -> Vec<Self> {
            let mut items = Self::values();
            items.shuffle(&mut thread_rng());
            items
        }
    }
    
    impl Shuffable for Color {
        fn values() -> Vec<Self> {
            vec![Color::Red, Color::Blue, Color::Green]
        }   
    }
    
    fn main() {
        let shuffled = Color::shuffled();
        println!("{:?}", shuffled)
    }
    

    Self has to be sized since the concrete type that is implementing Shuffable needs to be stored inside a vector and we need to know how much space we are allocating.

    I think the point of confusion may be with the generic parameter T, I assume you were thinking that this is for representing the Color type? If so, this is not necessary since the type for which you are implementing the trait is represented through Self. T introduces another type which is not constrained anywhere.