Description
I have experienced problems with Send
bounds on GATs. The original code where the problem occurred uses the async-trait
crate and looks as follows:
#![feature(generic_associated_types)]
use async_trait::async_trait;
use std::ops::Deref;
async fn some_async_task() {}
#[async_trait]
trait Source {
type T;
type Wrapper<'a>: Deref<Target = Self::T> + Send
where
Self: 'a;
async fn retrieve(&mut self) -> Self::Wrapper<'_>;
}
struct S {
state: i32,
}
impl S {
fn new() -> Self {
S { state: 0 }
}
}
#[async_trait]
impl Source for S {
type T = i32;
type Wrapper<'a> = &'a Self::T;
async fn retrieve(&mut self) -> Self::Wrapper<'_> {
self.state += 1;
&self.state
}
}
#[async_trait]
trait User {
// After updating nightly rustc, this won't work anymore:
async fn process<'a, 'b, S>(
&'a self,
source: &'b mut S,
) -> S::Wrapper<'b>
where
S: Source + Send,
{
let result = source.retrieve().await;
some_async_task().await;
result
}
}
struct U {}
#[async_trait]
impl User for U {}
#[tokio::main]
async fn main() {
let mut s = S::new();
let u = U {};
let value: &i32 = u.process(&mut s).await;
println!("Result = {}", value);
}
Using the Rust playground with Nightly version: 1.58.0-nightly (2021-11-07 46b8e74), I get:
Compiling playground v0.0.1 (/playground)
error[E0311]: the parameter type `S` may not live long enough
--> src/main.rs:46:5
|
40 | async fn process<'a, 'b, S>(
| - help: consider adding an explicit lifetime bound...: `S: 'c`
...
46 | / {
47 | | let result = source.retrieve().await;
48 | | some_async_task().await;
49 | | result
50 | | }
| |_____^ ...so that the type `S` will meet its required lifetime bounds
error: could not compile `playground` due to previous error
If I follow the compiler's advice literally, then of course I'll get:
error[E0261]: use of undeclared lifetime name `'c`
If I declare 'c as a lifetime parameter, then the compiler demands I should add a bound S: 'd, and so on.
This code worked with some earlier version of nightly Rust (without the where Self: 'a
bound, and before where Self: 'a
was required in the GAT). Unfortunately I do not remember which version that was (a few weeks ago).
However, even before the update of rustc, I had difficulties to remove the Send
bound from the GATs definition and to include a bound in another method that uses the GATs. This problem still persists and can be demonstrated with the following code:
#![feature(generic_associated_types)]
use async_trait::async_trait;
use std::ops::Deref;
async fn some_async_task() {}
#[async_trait]
trait Source {
type T;
// I removed the `Send` bound here:
type Wrapper<'a>: Deref<Target = Self::T>
where
Self: 'a;
async fn retrieve(&mut self) -> Self::Wrapper<'_>;
}
struct S {
state: i32,
}
impl S {
fn new() -> Self {
S { state: 0 }
}
}
#[async_trait]
impl Source for S {
type T = i32;
type Wrapper<'a> = &'a Self::T;
async fn retrieve(&mut self) -> Self::Wrapper<'_> {
self.state += 1;
&self.state
}
}
#[async_trait]
trait User {
async fn process<'a, 'b, S>(
&'a self,
source: &'b mut S,
) -> S::Wrapper<'b>
where
S: Source + Send + 'static,
// And added it here:
<S as Source>::Wrapper<'b>: Send,
{
let result = source.retrieve().await;
some_async_task().await;
result
}
}
struct U {}
#[async_trait]
impl User for U {}
#[tokio::main]
async fn main() {
let mut s = S::new();
let u = U {};
let value: &i32 = u.process(&mut s).await;
println!("Result = {}", value);
}
Using again Rust playground with Nightly version: 1.58.0-nightly (2021-11-07 46b8e74), I get:
Compiling playground v0.0.1 (/playground)
error: implementation of `Send` is not general enough
--> src/main.rs:48:5
|
48 | / {
49 | | let result = source.retrieve().await;
50 | | some_async_task().await;
51 | | result
52 | | }
| |_____^ implementation of `Send` is not general enough
|
= note: `<S as Source>::Wrapper<'0>` must implement `Send`, for any lifetime `'0`...
= note: ...but `Send` is actually implemented for the type `<S as Source>::Wrapper<'b>`
error: could not compile `playground` due to previous error
If I try to use a HRTB (for<'z> <S as Source>::Wrapper<'z>: Send
), then I get:
Compiling playground v0.0.1 (/playground)
error[E0277]: `<_ as Source>::Wrapper<'z>` cannot be sent between threads safely
--> src/main.rs:64:25
|
64 | let value: &i32 = u.process(&mut s).await;
| ^^^^^^^ `<_ as Source>::Wrapper<'z>` cannot be sent between threads safely
|
= help: the trait `for<'z> Send` is not implemented for `<_ as Source>::Wrapper<'z>`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to previous error
I'm not sure if these problems are related, but there seem to be a link. There has been a discussion on the Rust users forum where another smaller example (without async-trait
macros) was created:
#![feature(generic_associated_types)]
use std::{future::Future, marker::PhantomData};
trait Trait {
type Associated<'a>: Send
where
Self: 'a;
}
fn future<'a, S: Trait + 'a, F>(f: F) -> F
where
F: Future<Output = ()> + Send,
{
f
}
fn foo<'a, S: Trait + 'a>() {
future::<'a, S, _>(async move {
let result: PhantomData<S::Associated<'a>> = PhantomData;
async {}.await;
});
}
This results (using the same compiler version) in:
Compiling playground v0.0.1 (/playground)
error[E0311]: the parameter type `S` may not live long enough
--> src/lib.rs:18:5
|
17 | fn foo<'a, S: Trait + 'a>() {
| -- help: consider adding an explicit lifetime bound...: `S: 'b +`
18 | future::<'a, S, _>(async move {
| ^^^^^^^^^^^^^^^^^^ ...so that the type `S` will meet its required lifetime bounds...
|
note: ...that is required by this bound
--> src/lib.rs:12:30
|
12 | F: Future<Output = ()> + Send,
| ^^^^
error: could not compile `playground` due to previous error
If I replace Send
with SomeTrait
with impl<T> SomeTrait for T {}
, the code will compile. We guessed the problem might be auto-trait related?
See also: