I'm new to rust's async programming and encountered something weird. Here's minimized code (nightly):
#![feature(type_alias_impl_trait)]
use std::path::Path;
use std::future::Future;
type Error = Box<dyn std::error::Error>;
#[tokio::main]
async fn main() -> Result<(), Error> {
tokio::spawn(async move {
"test".to_owned().super_async("x").await
});
Ok(())
}
trait AsyncTrait {
type Fut<'a, P>: Future<Output=()>
where
Self: 'a,
P: AsRef<Path> + 'a;
fn test_async<P: AsRef<Path>>(&self, p: P) -> Self::Fut<'_, P>;
}
impl AsyncTrait for String {
type Fut<'a, P> = impl Future<Output = ()> + 'a
where
Self: 'a,
P: AsRef<Path> + 'a;
fn test_async<P: AsRef<Path>>(&self, p: P) -> Self::Fut<'_, P> {
async move {
let bs = p.as_ref();
()
}
}
}
trait SuperAsync: AsyncTrait {
type SuperFut<'a, P>: Future<Output=()>
where
P: AsRef<Path> + 'a,
Self: 'a;
fn super_async<P: AsRef<Path>>(&self, p: P) -> Self::SuperFut<'_, P>;
}
impl<T: AsyncTrait> SuperAsync for T {
type SuperFut<'a, P> = impl Future<Output = ()> + 'a
where
P: AsRef<Path> + 'a,
Self: 'a;
fn super_async<P: AsRef<Path>>(&self, p: P) -> Self::SuperFut<'_, P> {
async move {
self.test_async(p).await;
()
}
}
}
Then I got the error message:
error: implementation of `AsRef` is not general enough
--> src/main.rs:45:5
|
45 | / tokio::spawn(async move {
46 | | "test".to_owned().super_async("x").await
47 | | });
| |______^ implementation of `AsRef` is not general enough
|
= note: `AsRef<Path>` would have to be implemented for the type `&'0 str`, for any lifetime `'0`...
= note: ...but `AsRef<Path>` is actually implemented for the type `&'1 str`, for some specific lifetime `'1`
I don't understand the error message. If I remove tokio::spawn or impl SuperAsync only for String , then the error disappers. Any help?
I did not dig into the problem, but for this specific case, I believe you should restrain P with P: AsRef<Path> + 'a + ?Sized. This way, P is substituted with str instead of &str, which I believe is where the problem arose.
Working ver:
#![feature(type_alias_impl_trait)]
use std::path::Path;
use std::future::Future;
type Error = Box<dyn std::error::Error>;
#[tokio::main]
async fn main() -> Result<(), Error> {
tokio::spawn(async move {
String::from("test").super_async("x").await
});
Ok(())
}
trait AsyncTrait {
type Fut<'a, P>: Future<Output = ()>
where
Self: 'a,
P: AsRef<Path> + 'a + ?Sized;
fn test_async<'a, P: AsRef<Path> + ?Sized>(&'a self, p: &'a P) -> Self::Fut<'a, P>;
}
impl AsyncTrait for String {
type Fut<'a, P> = impl Future<Output = ()> + 'a
where
Self: 'a,
P: AsRef<Path> + 'a + ?Sized;
fn test_async<'a, P: AsRef<Path> + ?Sized>(&'a self, p: &'a P) -> Self::Fut<'a, P> {
async move {
let bs = p.as_ref();
()
}
}
}
trait SuperAsync: AsyncTrait {
type SuperFut<'a, P>: Future<Output=()>
where
P: AsRef<Path> + 'a + ?Sized,
Self: 'a;
fn super_async<'a, P: AsRef<Path> + ?Sized>(&'a self, p: &'a P) -> Self::SuperFut<'a, P>;
}
impl<T: AsyncTrait> SuperAsync for T {
type SuperFut<'a, P> = impl Future<Output = ()> + 'a
where
P: AsRef<Path> + 'a + ?Sized,
Self: 'a;
fn super_async<'a, P: AsRef<Path> + ?Sized>(&'a self, p: &'a P) -> Self::SuperFut<'a, P> {
async move {
self.test_async(p).await;
()
}
}
}
Thanks a lot! I also encountered some weird errors when using GAT and async together. How did you find the real problem is something related with the Send bound? I didn't realize this before.