The idea is to allow the user to specify a specific implementation of MatVecMul when they call solve. But it seems this is not allowed by the compiler. It complains:
error[E0562]: impl Trait not allowed outside of function and inherent method return types.
I was wondering what would be the correct way of realizing this idea?
It doesn't have exactly the same semantics as your code (no existential type stuff here), but would it be acceptable to use generics for your use case?
Actually, that’s almost exactly what the compiler would expand the original code to if it compiled. The problem with the original code is that impl MatVecMul is ambiguous; impl Trait means two different things depending on where it appears:
In an argument list, it’s syntax sugar for declaring a type parameter with the given trait bounds.
In a return type, it’s an existential type that’s guaranteed to satisfy the listed trait bounds, but is otherwise opaque.
In this case, it appears in both positions and the compiler can’t tell which you intended. The impl is being interpreted as part of the FnOnce return type, where it’s disallowed.
Edit: It looks like this also shows up when trying to return a closure that should have an opaque return type. You can get around that by defining your own trait that represents the properties you want the closure to have:
trait SolveFn: FnOnce(i32) -> <Self as SolveFn>::Result {
type Result: MatVecMul + Debug;
}
impl<M: MatVecMul + Debug, F: FnOnce(i32) -> M> SolveFn for F {
type Result = M;
}
fn solve(_: Vec<f64>, mul: impl SolveFn) -> f64 { /* ... */ }
fn make_closure() -> impl SolveFn { /* ... */ }
My immediate thought was if putting the full closure or its return in a Box would work. Sounds like there's more to consider though.
I had recently been comparing making a function to return a closure (Box) vs. using the partial macro vs. just accepting that I might have to make the same/similar closure in a few functions to avoid boxes... But cool to see that unboxed closures are being worked on!
This particular example does not demonstrate any features of the unboxed_closures feature other than that it enables the ability to explicitly name the Fn traits by syntax sugar free interface. I.e. writing Fn<(A,B), Output = C> instead of Fn(A, B) -> C. The ability to nest impl like this is readily available on stable and can be used with other traits like e.g. Iterator
fn print_all(it: impl IntoIterator<Item = impl AsRef<[u8]>>) {
println!("--------------------");
for slice in it {
println!("{:?}", slice.as_ref());
}
}
fn main() {
let x = vec!["hello".to_string(), "world".to_string()];
let y = [[1u8, 2, 3], [4, 5, 6]];
print_all(x);
print_all(&y);
}
It is “only” the fact that impl Trait syntax is not allowed in the return type of a Fn trait that makes this not work. But this is to be expected since those traits are not stabilized yet. As a matter of fact, it might (and in my personal opinion should) be considered to change the closure output type from an associated type into a type parameter, and impl Trait is currently not supported to occur nested in type parameters of another impl Trait, like e.g. x: impl PartialEq<impl Debug>>.
except for the fact that the latter two variants compile successfully on stable and the last one supports explicit passing of the generic type arguments R and F.