Borrow checker issue trying to pass a function pointer as paramerter - rust

I am seeking help to understand why the borrow checker fails for the following minimal non-working example, and I would be very happy to learn how to correctly implement what I was trying to do:
use std::collections::HashSet;
struct Foo {
data: HashSet<usize>
}
impl Foo {
fn test<'a, F, T>(&mut self, _operation: F) -> ()
where F: Fn(&'a HashSet<usize>, &'a HashSet<usize>) -> T,
T: Iterator<Item=&'a usize>
{
let update: HashSet<usize> = vec![4, 2, 9].into_iter().collect();
self.data = _operation(&self.data, &update).copied().collect();
}
fn new() -> Self {
Foo { data: HashSet::new() }
}
}
fn main() {
let mut foo: Foo = Foo::new();
foo.test(HashSet::intersection);
}
My main source of confusion is that, if I replace the call to _operation with HashSet::intersection, the code compiles. I thought that the type of the parameter _operation would allow me to pass both HashSet::intersection and HashSet::union as operations here.
For the record, this is the error I receive:
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src\main.rs:13:32
|
13 | self.data = _operation(&self.data, &update).copied().collect();
| ^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 8:23...
--> src\main.rs:8:23
|
8 | fn test<'a, F, T>(&mut self, _operation: F) -> ()
| ^^^^^^^^^
note: ...so that reference does not outlive borrowed content
--> src\main.rs:13:32
|
13 | self.data = _operation(&self.data, &update).copied().collect();
| ^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the method body at 8:13...
--> src\main.rs:8:13
|
8 | fn test<'a, F, T>(&mut self, _operation: F) -> ()
| ^^
note: ...so that reference does not outlive borrowed content
--> src\main.rs:13:32
|
13 | self.data = _operation(&self.data, &update).copied().collect();
| ^^^^^^^^^^
For more information about this error, try `rustc --explain E0495`.
error: could not compile `aoc06` due to previous error

The issue, as the (albeit cryptic) compiler message suggests, is that there is a lifetime mismatch: _operation expects HashSet references that live as long as 'a, but &self.data has a lifetime 'b, the elided lifetime of &mut self, and &update has a different lifetime that lasts the duration of the test function body.
To resolve this issue, we must specify that the function type F takes in HashMap references of arbitrary lifetimes, not just the specific lifetime 'a -- this lets the compiler infer the appropriate lifetime when _operation is invoked. This is why we need Higher-Rank Trait Bounds (HRTBs):
fn test<F, T>(&mut self, _operation: F) -> ()
where F: for<'a> Fn(&'a HashSet<usize>, &'a HashSet<usize>) -> T,
However, this raises another issue. How do we apply the higher-ranked lifetime 'a to the type parameter T? Unfortunately Rust does not support higher-kinded types, but we can get away with "abstracting" out the the function type F and the higher-kinded type T to a trait and an associated type on said trait.
trait Operation<'a, T: 'a> {
type Output: Iterator<Item = &'a T>;
fn operate(self, a: &'a HashSet<T>, b: &'a HashSet<T>) -> Self::Output;
}
The Operation trait represents an operation on two HashSets that returns an iterator over references, equivalent to the functions HashSet::union, HashSet::intersection, and the like. We can achieve this using the following impl, which ensures that HashSet::intersection and the like implement Operation:
impl<'a, T: 'a, I, F> Operation<'a, T> for F
where
I: Iterator<Item = &'a T>,
F: FnOnce(&'a HashSet<T>, &'a HashSet<T>) -> I,
{
type Output = I;
fn operate(self, a: &'a HashSet<T>, b: &'a HashSet<T>) -> Self::Output {
self(a, b)
}
}
We can then use a HRTB on the Operation trait instead, which does not require any nested higher-kinded types:
fn test(&mut self, _operation: impl for<'a> Operation<'a, usize>) -> () {
let update: HashSet<usize> = vec![4, 2, 9].into_iter().collect();
self.data = _operation.operate(&self.data, &update).copied().collect();
println!("{:?}", self.data);
}
Playground

The arguments you are passing do not match the lifetime which you declared the Fn bound with.
fn test<'a, F, T>(&mut self, _operation: F) -> ()
'a is some arbitrary lifetime that may be specified by the caller,
F: Fn(&'a HashSet<usize>, &'a HashSet<usize>) -> T,
which must be adequate for the references given to _operation,
let update: HashSet<usize> = vec![4, 2, 9].into_iter().collect();
self.data = _operation(&self.data, &update).copied().collect();
but here you pass in a borrow from self (whose lifetime is not specified to outlive 'a) and a borrow from update (which is a local variable, which cannot outlive 'a).
In order to correctly write this, you need to specify that _operation may be called with any lifetime (which thus includes the lifetimes of borrows of local variables). That's simple, by itself:
fn test<F, T>(&mut self, _operation: F) -> ()
where
F: for<'a> Fn(&'a HashSet<usize>, &'a HashSet<usize>) -> T,
Note that 'a is no longer a lifetime parameter of test. Instead it's part of the bound on F: you can read this for<'a> notation as “for any lifetime, which we will call 'a, F may be called as a function with references &'a ...”.
However, this isn't actually a solution, because you also have T: Iterator<Item = &'a usize>, which uses 'a again. It isn't currently possible to write a where clause that expresses this relationship, particularly as even without the item being a reference, the iterator will be borrowing the &'a HashSets.
This is an unfortunate limitation of current Rust — it also comes up in trying to write a function that takes an async function which borrows an input (which is structurally the same as your situation, with Future in place of Iterator). However, there is a workaround: you can define a trait for the function, which has a single lifetime parameter that links everything together. (This doesn't impose any extra work on the caller of your function, because the trait is blanket implemented for all suitable functions.)
Here's your code with such a trait added and the needed modifications to fn test():
use std::collections::HashSet;
trait IteratorCallback<'a> {
type Output: Iterator<Item = &'a usize> + 'a;
fn call(self, a: &'a HashSet<usize>, b: &'a HashSet<usize>) -> Self::Output;
}
impl<'a, F, T> IteratorCallback<'a> for F
where
F: FnOnce(&'a HashSet<usize>, &'a HashSet<usize>) -> T,
T: Iterator<Item = &'a usize> + 'a,
{
type Output = T;
fn call(self, a: &'a HashSet<usize>, b: &'a HashSet<usize>) -> T {
// Delegate to FnOnce
self(a, b)
}
}
struct Foo {
data: HashSet<usize>,
}
impl Foo {
fn test<F>(&mut self, _operation: F) -> ()
where
F: for<'a> IteratorCallback<'a>,
{
let update: HashSet<usize> = vec![4, 2, 9].into_iter().collect();
self.data = _operation.call(&self.data, &update).copied().collect();
}
fn new() -> Self {
Foo {
data: HashSet::new(),
}
}
}
fn main() {
let mut foo: Foo = Foo::new();
foo.test(HashSet::intersection);
}
Note: I changed the function bound to FnOnce because that's more permissive than Fn and all you need in this case, but the same technique will work with Fn as long as you change fn call(self, to fn call(&self,.
Credit: I used this Reddit comment by user Lej77 as an example to work from for the trait technique.

Related

Borrow Checker not releasing borrow from FnOnce callback

Why does this compile:
fn func<T>(
callback: impl FnOnce(&mut i64) -> T,
) -> T {
let v = 42;
callback(&mut 42)
}
but this not?:
fn func<'a, T>(
callback: impl FnOnce(&'a mut i64) -> T,
) -> T {
let v = 42;
callback(&mut 42)
}
Even this doesn't compile:
fn func<'a, T: 'static>(
callback: impl FnOnce(&'a mut i64) -> T,
) -> T {
let v = 42;
callback(&mut 42)
}
Is there a way to tell Rust that the T returned from the callback doesn't hold any references to 'a? I thought : 'static would forbid references in general but it doesn't seem to work. Unfortunately, I do need to give 'a a name because I'm using the lifetime elsewhere, the actual code is somewhat more complicated than this minimal example.
In your first snippet, the lifetimes are elided via Higher-Ranked Trait Bounds:
fn func<T>(
callback: impl for<'a> FnOnce(&'a mut i64) -> T,
) -> T {
let v = 42;
callback(&mut 42)
}
That means the closure can be called with any lifetime, and that includes the lifetime of v.
In your second snippet, the lifetime is chosen by the caller. This is not related to T: for example, the caller can choose 'static, then store the parameter in a static. There is just no way to satisfy this requirement with a variable defined in the function.

Can a trait's impl specify a lifetime that comes from a method's input argument?

For a type
pub struct Child<'a> {
buf: &'a mut [u8],
}
I can define a trait and implement the trait for the type but with a lifetime that is bound to a calling function's context (not to a local loop context):
pub trait MakeMut<'a> {
fn make_mut(buf: &'a mut [u8]) -> Self;
}
impl<'a> MakeMut<'a> for Child<'a> {
fn make_mut(buf: &'a mut [u8]) -> Self {
Self { buf }
}
}
And first to show a somewhat working example because x is only borrowed within the context of the loop because Child::make_mut is hardcoded in the map1 function:
pub fn map1<F>(mut func: F)
where
F: FnMut(&mut Child),
{
let mut vec = vec![0; 16];
let x = &mut vec;
for i in 0..2 {
let offset = i * 8;
let s = &mut x[offset..];
let mut w = Child::make_mut(s);
func(&mut w);
}
}
But in trying to make map2, a generic version of map1 where the T is bound to the MakeMut trait but with lifetime of the entire function body, this won't compile, for good reasons (the T lifetimes that would be created by T: MakeMut<'a> have the lifetime of map2, not the inner loop):
pub fn map2<'a, F, T>(mut func: F) // lifetime `'a` defined here
where
T: MakeMut<'a>,
F: FnMut(&mut T),
{
let mut vec = vec![0; 16];
let x = &mut vec;
for i in 0..2 {
let offset = i * 8;
let s = &mut x[offset..];
let mut w = T::make_mut(s); // error: argument requires that `*x` is borrowed for `'a`
func(&mut w);
}
}
I want to do something almost like this but of course it doesn't compile either:
pub trait MakeMut {
fn make_mut<'a>(buf: &'a mut [u8]) -> Self;
}
impl<'a> MakeMut for Child<'a> {
fn make_mut(buf: &'a mut [u8]) -> Self { // lifetime mismatch
Self{ buf }
}
}
with the compiler errors:
error[E0308]: method not compatible with trait
--> src/main.rs:45:5
|
45 | fn make_mut(buf: &'a mut [u8]) -> Self {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
|
= note: expected fn pointer `fn(&'a mut [u8]) -> Child<'_>`
found fn pointer `fn(&'a mut [u8]) -> Child<'_>`
note: the lifetime `'a` as defined here...
--> src/main.rs:45:5
|
45 | fn make_mut(buf: &'a mut [u8]) -> Self {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...does not necessarily outlive the lifetime `'a` as defined here
--> src/main.rs:44:6
|
44 | impl<'a> MakeMut for Child<'a> {
| ^^
Is there a syntax that allows a trait for a Child<'a> where the 'a is defined by the input argument to the method make_mut? So a generic function could be defined for a trait that returns an instance but where the instance lifetime is not the entire function, but just a shorter lifetime defined by an inner block?
I understand the lifetime is part of the type being returned, but it almost seems like a higher-ranked trait bound (HRTB) would suite this problem except I haven't found a way to specify the lifetime that suites the trait and the method signatures.
Here is a playground link https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=fb28d6da9d89fde645edeb1ca0ae5b21
Your first attempt is close to what you want. For reference:
pub trait MakeMut<'a> {
fn make_mut(buf: &'a mut [u8]) -> Self;
}
impl<'a> MakeMut<'a> for Child<'a> {
fn make_mut(buf: &'a mut [u8]) -> Self {
Self { buf }
}
}
The first problem is the bound on T in map2:
pub fn map2<'a, F, T>(mut func: F)
where
T: MakeMut<'a>,
F: FnMut(&mut T),
This requires the compiler to deduce a single 'a that applies for the whole function. Since lifetime parameters come from outside of the function, the lifetime 'a is necessarily longer than the function invocation, which means anything with lifetime 'a has to outlive the function. Working backwards from the T::make_mut() call, the compiler eventually deduces that x is &'a mut Vec<_> which means vec has to outlive the function invocation, but there's no possible way it can since it's a local.
This can be fixed by using a higher-rank trait bound indicating that T has to implement MakeMut<'a> for any possible lifetime 'a, which is expressed like this:
pub fn map2<F, T>(mut func: F)
where
T: for<'a> MakeMut<'a>,
F: FnMut(&mut T),
With this change, the code compiles.
What you'll then find is that you can't ever actually call map2 with T=Child<'_> because you'll run into the same problem in a different place. The caller must specify a specific lifetime for 'a in Child<'a>, but this disagrees with the HRTB -- you have impl<'a> MakeMut<'a> for Child<'a> but the HRTB wants impl<'a, 'b> MakeMut<'b> for Child<'a>, and that brings back the lifetime problem in that implementation's make_mut.
One way around this is to decouple the implementation of MakeMut from Child, providing a "factory type" that uses associated types. This way, the caller doesn't have to supply any pesky lifetime argument that could cause trouble later.
pub trait MakeMut<'a> {
type Item;
fn make_mut(buf: &'a mut [u8]) -> Self::Item;
}
struct ChildFactory;
impl<'a> MakeMut<'a> for ChildFactory {
type Item = Child<'a>;
fn make_mut(buf: &'a mut [u8]) -> Child<'a> {
Child { buf }
}
}
Then we modify map2 to be aware of the associated type:
pub fn map2<F, T>(mut func: F)
where
T: for<'a> MakeMut<'a>,
F: for<'a, 'b> FnMut(&'b mut <T as MakeMut<'a>>::Item),
whew
Now, finally, we can use map2:
map2::<_, ChildFactory>(|v| {});
(Playground)

Why explicitly non-dispatchable methods in Iterator are dispatchable?

Rust reference object-safety confused me for a while, and says:
Explicitly non-dispatchable functions require:
Have a where Self: Sized bound (receiver type of Self (i.e. self) implies this).
But I found code::iter::Iterator has dozen of methods are declared as explicitly non-dispatchable functions, one of them below:
pub trait Iterator {
...
fn count(self) -> usize
where
Self: Sized,
{
self.fold(
0,
#[rustc_inherit_overflow_checks]
|count, _| count + 1,
)
}
...
}
However, all of them are dispatchable by trait-object at rust-playground:
fn main() {
let it: &mut dyn Iterator<Item = u32> = &mut [1, 2, 3].into_iter();
assert_eq!(3, it.count()); // ok
}
That is good, I start try to implements a worked example, but it can not be dispatched at rust-playground, and report compiler error: "the dispatch method cannot be invoked on a trait object" that is expected:
fn main() {
pub trait Sup {
fn dispatch(self) -> String
where
Self: Sized,
{
"sup".to_string()
}
}
struct Sub;
impl Sup for Sub {
fn dispatch(self) -> String {
"sub".to_string()
}
}
let it: &mut dyn Sup = &mut Sub;
assert_eq!("trait", it.dispatch());
}
Why explicitly non-dispatchable methods in code::iter::Iterator are dispatchable? Is there any magic which I didn't found?
The reason is simple, if we think of this: what type we're invoking the method count on?
Is it dyn Iterator<Item = u32>? Let's check:
assert_eq!(3, <dyn Iterator<Item = u32>>::count(it));
Nope, there are two errors:
error[E0308]: mismatched types
--> src/main.rs:3:53
|
3 | assert_eq!(3, <dyn Iterator<Item = u32>>::count(it));
| ^^ expected trait object `dyn Iterator`, found mutable reference
|
= note: expected trait object `dyn Iterator<Item = u32>`
found mutable reference `&mut dyn Iterator<Item = u32>`
error[E0277]: the size for values of type `dyn Iterator<Item = u32>` cannot be known at compilation time
--> src/main.rs:3:53
|
3 | assert_eq!(3, <dyn Iterator<Item = u32>>::count(it));
| --------------------------------- ^^ doesn't have a size known at compile-time
| |
| required by a bound introduced by this call
|
= help: the trait `Sized` is not implemented for `dyn Iterator<Item = u32>`
note: required by a bound in `count`
OK, well... is it &mut dyn Iterator, then?
assert_eq!(3, <&mut dyn Iterator<Item = u32>>::count(it));
Now it compiles. It's understandable that the second error goes away - &mut T is always Sized. But why do the &mut dyn Iterator has access to the method of Iterator?
The answer is in the documentation. First, dyn Iterator obviously implements Iterator - that's true for any trait. Second, there's implementation of Iterator for any &mut I, where I: Iterator + ?Sized - which our dyn Iterator satisfies.
Now, one may ask - what code is executed when we use this implementation? After all, count requires consuming self, so calling it on reference can't delegate to the dyn Iterator - otherwise we'd be back to square one, dispatching non-dispatchable.
Here, the answer lies in the structure of the Iterator trait. As one can see, it has only a single required method, namely next, which takes &mut self; all other methods are provided, that is, they have some default implementations using next - for example, here's it for count:
fn count(self) -> usize
where
Self: Sized,
{
self.fold(
0,
#[rustc_inherit_overflow_checks]
|count, _| count + 1,
)
}
where fold, in turn, is the following:
fn fold<B, F>(mut self, init: B, mut f: F) -> B
where
Self: Sized,
F: FnMut(B, Self::Item) -> B,
{
let mut accum = init;
while let Some(x) = self.next() {
accum = f(accum, x);
}
accum
}
As you can see, knowing just the next, compiler can derive fold and then count.
Now, back to our &mut dyn Iterators. Let's check how, exactly, this implementation looks like; it appears to be quite simple:
#[stable(feature = "rust1", since = "1.0.0")]
impl<I: Iterator + ?Sized> Iterator for &mut I {
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
(**self).next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
(**self).size_hint()
}
fn advance_by(&mut self, n: usize) -> Result<(), usize> {
(**self).advance_by(n)
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
(**self).nth(n)
}
}
You can see that the &self and &mut self methods, i.e. the ones which can be called on the trait object, are forwarded by the reference to the inner value and dispatched dynamically.
The self methods, i.e. the ones which cannot use the trait object, are dispached statically using their default implementation, which consume the reference and pass it, eventually, into one of these - non-consuming, dynamically-dispatched - methods.

lifetime of function pointer is for<'a, '_> while it should be for<'r>

Sometimes I struggle with lifetimes. I'm still learning and I don't know what's happening here:
use std::future::Future;
use futures::future::{BoxFuture, FutureExt};
struct M{}
struct Client{}
impl Client {
async fn send_and_expect<'a>(
&'a mut self,
m: &M
) -> std::result::Result<(), ()> {
Ok(())
}
pub fn connection_retrier<'a, T>(
f: fn(&'a mut Self, &M) -> T,
f_self: &'a mut Self,
f_m: &'a M,
)-> BoxFuture<'a, std::result::Result<(),()>>
where
T: Future<Output = std::result::Result<(), ()>> + 'a
{
async move {
Client::send_and_expect(f_self, f_m).await
}.boxed()
}
async fn send_with_retry<'a>(&'a mut self) -> std::result::Result<(), ()> {
let m = M{};
Client::connection_retrier(
Client::send_and_expect,
&mut *self, &m).await
}
}
Error:
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/lib.rs:31:21
|
11 | ) -> std::result::Result<(), ()> {
| --------------------------- the `Output` of this `async fn`'s found opaque type
...
31 | Client::send_and_expect,
| ^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected fn pointer `for<'r> fn(&mut Client, &'r M) -> impl futures::Future`
found fn pointer `for<'a, '_> fn(&'a mut Client, &M) -> impl futures::Future
Playground
I'm now completely confused about why in for<'r> fn(&mut Client, &'r M) -> impl futures::Future, &mut Client has no lifetime. And what does _ means in for<'a, '_> fn(&'a mut Client, &M) -> impl futures::Future`?
I'm very intersted in learning what's happening here.
Cleaning up the code
In order to see this code from the compiler's perspective, let's change all the lifetime parameters in this example to be explicit and have distinct names, expand the async fn sugar, and then look at how the error changes.
The above example is equivalent to the following after lifetime inference and desugaring.
// name the second lifetime, and expand the async fn sugar.
fn send_and_expect<'a, 'b>(&'a mut self, m: &'b M) -> impl Future<Item=Result<(), ()>> + 'a + 'b
{ ... }
// rename 'a to 'c to avoid ambiguous lifetime names
pub fn connection_retrier<'c, T>(
f: for<'d> fn(&'c mut Self, &'d M) -> T, // name the implicit higher-ranked lifetime here
f_self: &'c mut Self,
f_m: &'c M,
)-> BoxFuture<'c, std::result::Result<(), ()>>
where T: Future<Output = std::result::Result<(), ()>> + 'c
{ ... }
// rename 'a to 'e to avoid ambiguous lifetime names
async fn send_with_retry<'e>(&'e mut self) -> std::result::Result<(), ()> {
After making this change, the error becomes:
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/lib.rs:31:21
|
11 | ) -> std::result::Result<(), ()> {
| --------------------------- the `Output` of this `async fn`'s found opaque type
...
31 | Client::send_and_expect,
| ^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected fn pointer `for<'d> fn(&mut Client, &'d M) -> impl futures::Future`
found fn pointer `for<'a, 'b> fn(&'a mut Client, &'b M) -> impl futures::Future`
This should clarify your question about '_: it's just the name the compiler gave the inferred second lifetime parameter of send_and_expect. As for the missing lifetime on &mut Client, you can see that it's still missing here. For reasons I do not completely understand, and in ways that depend on the exact error message given, the compiler will sometimes omit concrete lifetimes when printing the type of references, but make no mistake, the lifetime of that reference is 'c.
Solving the error
Onto the actual problem. The signature of f indicates that connection_retrier is expecting a function which (1) takes a reference of lifetime &'c and (2) a reference of any other lifetime, and (3) returns a Future type which will remain valid as long as 'c does, as specified by your where bound.
When we pass send_and_expect to connection_retrier, in order for the signatures to match the compiler is coercing it to the type for<'d> send_and_expect::<'c, 'd>. But that type doesn't meet condition (3) above! Unlike a regular function, the default behavior of an async function is to capture all input lifetimes in its return type, so the return type of for<'d> send_and_expect::<'c, 'd> is in fact impl Future<Item=Result<(), ()>> + 'c + 'd, as you can tell by looking at send_and_expect's expanded signature.
Since this type borrows from the two lifetimes 'c and 'd, and there is no constraint that 'd: 'c (read: 'd outlives 'c), it may not remain valid for the entirety of the lifetime 'c, if 'd ends first. It is this mismatch that results in the rather cryptic lifetime error you received. There are two ways you can solve this problem, depending on your preferred semantics. You can either:
Remove the higher-ranked bound from f entirely, and specify exactly the lifetimes you will be calling it with in connection_retrier (both &'c.)
pub fn connection_retrier<'c, T>(
f: fn(&'c mut Self, &'c M) -> T
f_self: &'c mut Self,
f_m: &'c M,
) -> BoxFuture<'c, std::result::Result<(), ()>>
where T: Future<Output = std::result::Result<(), ()>> + 'c
{ ... }
Keep the signature of connection_retrier the same and specify that the future returned by send_and_expect only borrows from its first argument. To do this, you will need to drop the async fn sugar on the signature and wrap the body in an async move block.
fn send_and_expect<'a, 'b>(&'a mut self, m: &'b M) -> impl Future<Output=Result<(), ()>> + 'a
{ async move { ... } }
Note that you cannot solve this by writing the type of f as for<'d: 'c> fn(&'c mut Self, &'d M) -> T, as bounds are currently not permitted for universally quantified lifetimes.

Generic for FnOnce that returns a future with a lifetime

I have a struct that needs a callback that returns a future whose output has a lifetime:
struct Foo;
struct Bar;
struct Baz<F>
where F: for<'a> FnOnce(&'a Foo) -> impl std::future::Future<Output=&'a Bar> // ?
{
cb: F
}
That doesn't compile, with a syntax error since impl can't appear in trait bounds:
Compiling playground v0.0.1 (/playground)
error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
--> src/lib.rs:6:37
|
6 | where F: for<'a> FnOnce(&'a Foo) -> impl std::future::Future<Output=&'a Bar>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
This doesn't compile either:
struct Foo;
struct Bar;
struct Baz<O, F>
where
O: for<'a> std::future::Future<Output=&'a Bar>,
F: for<'b> FnOnce(&'b Foo) -> O,
{
cb: F
}
Complaining that 'a isn't recognized (and the lifetimes 'a and 'b would be unrelated):
Compiling playground v0.0.1 (/playground)
error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types
--> src/lib.rs:7:36
|
7 | O: for<'a> std::future::Future<Output=&'a Bar>,
| ^^^^^^^^^^^^^^
error: aborting due to previous error
Is there a way to specify this relationship?
It's a bit ugly, but this is the best way I could find (using struct Foo(Bar) for demonstration):
Playground
use std::future::Future;
struct Bar;
struct Foo(Bar);
trait FooClosure<'a> {
type Fut: Future<Output = &'a Bar>;
fn call(self, foo: &'a Foo) -> Self::Fut;
}
impl<'a, Fut: Future<Output = &'a Bar>, C: FnOnce(&'a Foo) -> Fut> FooClosure<'a> for C {
type Fut = Fut;
fn call(self, foo: &'a Foo) -> Fut {
self(foo)
}
}
struct Baz<F: for<'a> FooClosure<'a>>(F);
fn main() {
let closure: Box<dyn for<'a> FnOnce(&'a Foo) -> futures::future::Ready<&'a Bar>> =
Box::new(|foo| futures::future::ready(&foo.0));
let baz = Baz(closure);
}
I couldn't get the compiler to infer the types properly, so I had to wrap the closure as boxed trait object. Theoretically this shouldn't be necessary, and there might be a way to avoid this but I couldn't figure it out.
Note: I think there are plans to make your original code work without needing this trait black magic.
A straightforward way to do this is to parameterize the struct Baz itself with the lifetime parameter:
struct Baz<'a, O: Future<Output=&'a Bar>, F: FnOnce(&'a Foo) -> O> {
cb: F,
phantom: PhantomData<&'a O>,
}
The PhantomData instance is required to "use" the lifetime parameter 'a and the output type parameter O. You can think of O and 'a as components of the closure type F -- they will always be implicitly inferred from the lifetime and return type used by F.
Playground

Resources