Search code examples
rustassociated-typestrait-objectsdynamic-dispatch

Trait object as associated type of a trait object


I wrote two structs that implement a common trait Solve. Solve has an associated type Answer with trait bound Display. I want the function create_solver to return a trait object of Solve.

I need help with writing the associate type Answer = ??.

use std::fmt::Display;

fn main() {
    let solver = create_solver(2);
    
    let answer = solver.solve();
    
    println!("{}", answer);
}

fn create_solver(kind: u32) -> Box<dyn Solve<Answer = ??>> {
    match kind {
        1 => Box::new(SolverOne),
        2 => Box::new(SolverTwo),
        _ => unreachable!()
    }
}

trait Solve {
    type Answer: Display;
    
    fn solve(&self) -> Self::Answer;
}

struct SolverOne;

impl Solve for SolverOne {
    type Answer = u32;

    fn solve(&self) -> Self::Answer {
        3
    }
}

struct SolverTwo;

impl Solve for SolverTwo {
    type Answer = String;
    
    fn solve(&self) -> Self::Answer {
        String::from("three")
    }
}

I tried setting Answer = Box<dyn Display> but that results in error.

fn create_solver(kind: u32) -> Box<dyn Solve<Answer = Box<dyn Display>>> {
    match kind {
        1 => Box::new(SolverOne),
        2 => Box::new(SolverTwo),
        _ => unreachable!()
    }
}

trait Solve {
    type Answer: Display;
    
    fn solve(&self) -> Self::Answer;
}

struct SolverOne;

impl Solve for SolverOne {
    type Answer = Box<u32>;

    fn solve(&self) -> Self::Answer {
        Box::new(3)
    }
}

struct SolverTwo;

impl Solve for SolverTwo {
    type Answer = Box<String>;
    
    fn solve(&self) -> Self::Answer {
        Box::new(String::from("three"))
    }
}

Error message:

error[E0271]: type mismatch resolving `<SolverOne as Solve>::Answer == Box<(dyn Display + 'static)>`
  --> src/main.rs:13:14
   |
13 |         1 => Box::new(SolverOne),
   |              ^^^^^^^^^^^^^^^^^^^ type mismatch resolving `<SolverOne as Solve>::Answer == Box<(dyn Display + 'static)>`
   |
note: expected this to be `Box<(dyn std::fmt::Display + 'static)>`
  --> src/main.rs:28:19
   |
28 |     type Answer = Box<u32>;
   |                   ^^^^^^^^
   = note: expected struct `Box<(dyn std::fmt::Display + 'static)>`
              found struct `Box<u32>`
   = note: required for the cast from `SolverOne` to the object type `dyn Solve<Answer = Box<(dyn std::fmt::Display + 'static)>>`

error[E0271]: type mismatch resolving `<SolverTwo as Solve>::Answer == Box<(dyn Display + 'static)>`
  --> src/main.rs:12:5
   |
12 | /     match kind {
13 | |         1 => Box::new(SolverOne),
14 | |         2 => Box::new(SolverTwo),
15 | |         _ => unreachable!()
16 | |     }
   | |_____^ type mismatch resolving `<SolverTwo as Solve>::Answer == Box<(dyn Display + 'static)>`
   |
note: expected this to be `Box<(dyn std::fmt::Display + 'static)>`
  --> src/main.rs:38:19
   |
38 |     type Answer = Box<String>;
   |                   ^^^^^^^^^^^
   = note: expected struct `Box<(dyn std::fmt::Display + 'static)>`
              found struct `Box<String>`
   = note: required for the cast from `SolverTwo` to the object type `dyn Solve<Answer = Box<(dyn std::fmt::Display + 'static)>>`

For more information about this error, try `rustc --explain E0271`

What should I set as Answer = to make this work?


Solution

  • The associated type has to be the same for all types returned, and you have no such associated type. But you can create a wrapper type that using type erasure returns Box<dyn Display>:

    fn create_solver(kind: u32) -> Box<dyn Solve<Answer = Box<dyn Display>>> {
        struct ErasedSolver<T>(T);
        impl<T: Solve> Solve for ErasedSolver<T>
        where
            T::Answer: 'static,
        {
            type Answer = Box<dyn Display>;
    
            fn solve(&self) -> Self::Answer {
                Box::new(self.0.solve())
            }
        }
    
        match kind {
            1 => Box::new(ErasedSolver(SolverOne)),
            2 => Box::new(ErasedSolver(SolverTwo)),
            _ => unreachable!(),
        }
    }