Reputation: 351
I was working on a structure that had asynchronous functions as fields, but it didn't work when it took mutable references.
this works:
#![allow(dead_code)]
pub struct TestStruct;
struct TestStruct2<F>
where
F: std::future::Future<Output = ()>,
{
foo: fn(TestStruct) -> F,
}
async fn foo(_x: TestStruct) {}
#[tokio::main]
async fn main() {
let _s = TestStruct2 { foo };
}
this does not work:
#![allow(dead_code)]
pub struct TestStruct;
struct TestStruct2<F>
where
F: std::future::Future<Output = ()>,
{
foo: fn(&mut TestStruct) -> F,
}
async fn foo(_x: &mut TestStruct) {}
#[tokio::main]
async fn main() {
let _s = TestStruct2 { foo }; //error here
}
error:
mismatched types
expected fn pointer `for<'r> fn(&'r mut TestStruct) -> _`
found fn item `for<'r> fn(&'r mut TestStruct) -> impl std::future::Future<Output = ()> {foo}`
could someone explain to me why this happens and what can I do about it?
Upvotes: 2
Views: 622
Reputation: 65822
It looks like foo
's return type has an implicit + 'a
bound, where 'a
is the anonymous lifetime in &mut TestStruct
.
Simply desugaring foo
to an explicit impl Trait
type and an async
block solves the issue.
#![allow(dead_code)]
pub struct TestStruct;
struct TestStruct2<F>
where
F: std::future::Future<Output = ()>,
{
foo: fn(&mut TestStruct) -> F,
}
fn foo(_x: &mut TestStruct) -> impl std::future::Future<Output = ()> {
async {}
}
#[tokio::main]
async fn main() {
let _s = TestStruct2 { foo };
}
However, as soon as we try to use the reference from within the async
block, errors pop up again. (It looks like the compiler moves all parameters from an async fn
to the hidden async
block.)
fn foo(_x: &mut TestStruct) -> impl std::future::Future<Output = ()> {
async {
_x;
}
}
error[E0700]: hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds
--> src/main.rs:13:5
|
12 | fn foo(_x: &mut TestStruct) -> impl std::future::Future<Output = ()> {
| --------------- hidden type `impl Future<Output = ()>` captures the anonymous lifetime defined here
13 | / async {
14 | | _x;
15 | | }
| |_____^
|
help: to declare that the `impl Trait` captures `'_`, you can add an explicit `'_` lifetime bound
|
12 | fn foo(_x: &mut TestStruct) -> impl std::future::Future<Output = ()> + '_ {
| ++++
And if we apply the compiler's suggestion, then we're back to square one:
fn foo(_x: &mut TestStruct) -> impl std::future::Future<Output = ()> + '_ {
async {
_x;
}
}
error[E0308]: mismatched types
--> src/main.rs:20:28
|
20 | let _s = TestStruct2 { foo };
| ^^^ one type is more general than the other
|
= note: expected fn pointer `for<'r> fn(&'r mut TestStruct) -> _`
found fn item `for<'r> fn(&'r mut TestStruct) -> impl for<'r> Future<Output = ()> {foo}`
We can fix this by adding a lifetime parameter to TestStruct2
and using it in the appropriate places. (We can also restore foo
to its original form.)
#![allow(dead_code)]
pub struct TestStruct;
struct TestStruct2<'a, F>
where
F: std::future::Future<Output = ()> + 'a,
{
foo: fn(&'a mut TestStruct) -> F,
}
async fn foo(_x: &mut TestStruct) {}
#[tokio::main]
async fn main() {
let s = TestStruct2 { foo };
let mut t = TestStruct;
(s.foo)(&mut t).await;
}
Upvotes: 3
Reputation: 9152
The issue isn't the mut
, it's the reference and its lifetime. According to the Asynchronous Programming in Rust book, the return values of async
functions have their lifetimes bounded by their arguments' lifetimes. Your definition of TestStruct2::foo
doesn't accept this. To fix that, you can add this lifetime explicitly to F
:
struct TestStruct2<'a, F>
where
F: std::future::Future<Output = ()> + 'a,
{
foo: fn(&'a mut TestStruct) -> F,
}
Upvotes: 3