5

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?

1 Answer 1

7

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).

Sign up to request clarification or add additional context in comments.

2 Comments

I could find a workaround but I went into the Box problem. Thank you for your answer.
According to what you wrote, I found a working solution it is much simpler than I though initialy. Thank you very much for your explanation, it was clear as fresh water.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.