Search code examples
rusttrait-objects

expected trait object `dyn Responsability`, found type parameter `T`


I am trying to implement a responsability chain in Rust:

link to playground

use std::error::Error;

struct Query {
    query: String,
}

struct Response {
    response: u64,
}

trait Responsability {
    fn take(&self, iterator: std::slice::Iter<Box<dyn Responsability>>, query: Query) -> Result<Response, Box<dyn Error>>;
}

struct ResponsabilityChain<T: Responsability> {
    responsabilities: Vec<Box<T>>,
}

impl<T: Responsability> ResponsabilityChain<T>
where
    T: Responsability,
{
    pub fn new(responsabilities: Vec<T>) -> Self {
        let responsabilities = responsabilities.into_iter()
            .map(|elt| Box::new(elt))
            .collect();
        
        Self { responsabilities }
    }
    
    pub fn launch(&self, query: Query) -> Result<Response, Box<dyn Error>> {
        let iterator = self.responsabilities.iter();
        let responsability = iterator.next().unwrap();
        
        responsability.take(iterator, query)
    }
}

fn main() {
    println!("Hello, world!");
}

The infamous message is:

Compiling playground v0.0.1 (/playground) error[E0308]: mismatched types --> src/main.rs:35:29 | 19 | impl<T: Responsability> ResponsabilityChain | - this type parameter ... 35 |
responsability.take(iterator, query) |
^^^^^^^^ expected trait object dyn Responsability, found type parameter T | = note: expected struct std::slice::Iter<'_, Box<(dyn Responsability + 'static)>> found struct std::slice::Iter<'_, Box<T>> = help: type parameters must be constrained to match other types = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

For more information about this error, try rustc --explain E0308. error: could not compile playground due to previous error

I do not understand why the compiler complains expecting Box<dyn Responsability> while having Box<T> since I specify T: Responsability. What do I do wrong?


Solution

  • dyn I and <T> where T: I are different types in Rust, so the compiler complains since there's no implicit conversion.

    T is a concrete type determined at compile time. dyn I it is a "trait object", it is dynamic, and concrete type is unknown, but sort of carried within.

    A good video on the topic.

    Conversion from <T> where T: I to dyn I is not free, it has a runtime cost, so has to be explicit with the Rust's philosophy.

    The code could be fixed by using Vec<Box<dyn Responsability>> in all places. It will also allow you passing arbitrary types to new(), which is probably what you want, because Vec<T> has to contain objects of the same type (remember that this type is determined at compile time).