How do I express a constraint like
fn<F: 'static> apply_to_foos(f: F)
where
for<'a> F: fn(&'a mut Foo) -> impl Future<Output = Bar> + 'a
{
...
}
(The above expression doesn't work because constraints can't have impl
in them)
If this isn't possible, what can I do instead?
Using a heap allocated trait object we already can express that constraint with current compiler featuers (Rust 1.61).
fn apply_to_foos<F>(f: F)
where
for<'a> F: Fn(&'a mut Foo) -> Box<dyn Future<Output = Bar> + 'a>
{ /*...*/ }
Maybe this is already what we want to do, but for fun and games, let's say we want to use compile time polymorphy:
It is concievable that a future version of Rust would just allow us to replace the dyn
in the above signature and replace it with an impl
, while dropping the Box
altogether. FYI: The Box
is not needed for above signature to compile, but most real world usecase would require it in order to keep the trait object alive long enough.
Using an impl
here would require the compiler to support trait
s which can in turn depend on other traits
. Rust cannot do that, but trait
s can depend on concrete types. Let's introduce a type O
into our signature.
fn apply_to_foos<F, O>(f: F)
where
for<'a> F: Fn(&'a mut Foo) -> O, O: Future<Output = Bar>
{ /*...*/ }
This almost does what we want, but we have no way to transfer the lifetime requirement of 'a
to O
, since &'a
is local to the constraint of F
. We can factor out the entire constraint into its own trait however:
fn apply_to_foos<F, O>(f: F) where F: for<'a> FooToBar<'a> {}
with FooToBar
being declared like this (for example):
trait FooToBar<'a> {
type O: Future<Output = Bar> + 'a;
fn foo_to_bar(&self, foo: &'a mut Foo) -> Self::O;
}
Wether avoiding the heap allocation is worth the code bloat likely depends on your domain. I'd probably stick with the Box<dyn>
version most of the time.