I am confused that the function c is not a equivalent of function a or b. And therefore doesn't compile. What does it is the difference to have a generic type argument?
I think my question is what are the syntactic cues to decide between static and dynamic dispatch.
pub fn a<InType>(
inputs: &(impl AsRef<[InType]> + ?Sized),
) where
InType: AsRef<[f32]>,
{
let i = inputs.as_ref()[0].as_ref()[0];
println!("{i}");
}
pub fn b<InType: AsRef<[f32]>>(
inputs: &(impl AsRef<[InType]> + ?Sized),
) {
let i = inputs.as_ref()[0].as_ref()[0];
println!("{i}");
}
pub fn c(
inputs: &(impl AsRef<[dyn AsRef<[f32]>]> + ?Sized),
) {
let i = inputs.as_ref()[0].as_ref()[0];
println!("{i}");
}
fn main() {
let ivv = vec![vec![0f32;16];4];
a(&ivv);
b(&ivv);
c(&ivv);
}
The problem is that type [dyn AsRef<[f32]>] cannot exist. Trait object must be stored behind some reference. And you want here a slice of bare trait objects.
They also allow different things to the caller of the function. In function a and b there needs to be one "InType" which all the elements in the array have. With type c (if you do it correctly) every element in the array could have a different type, they just all have to implement AsRef<[f32]>.
Different types can have different sizes, so they can't be put next to each other normally. There has to be some kind of indirection via a reference or Box.
i think this blog post helped me to understand that impl is mostly like a generic parameter.
what confused me is that one cannot simply nest traits, but one needs to refer to them through generic parameters. with the limitation that impl cannot be nested it is far less powerful then generic parameters.
and @luca3s pointed out a usecase for dynamic dispatch.