I am trying to implement a responsability chain in Rust:
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 objectdyn Responsability
, found type parameterT
| = note: expected structstd::slice::Iter<'_, Box<(dyn Responsability + 'static)>>
found structstd::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-parametersFor more information about this error, try
rustc --explain E0308
. error: could not compileplayground
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?
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).