Implementation of `AsRef` is not general enough

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;
            ()
        }
    }
}

4 Likes

I minimized the code to the point you might not even recognize it, and without TAIT too. Here is it:

fn main() {
    let f = async move {
        let _f = i32::make::<&i32>();
        async {}.await
    };

    is_send(f); // Ok

    let f = super_async::<i32, &i32>();

    is_send(f); // Ok

    let f = async move {
        super_async::<i32, &i32>().await;
    };

    is_send(f); // Bad
}

fn is_send<T: Send>(_: T) {}

trait AsyncTrait {
    type Fut<'a, P>
    where
        P: 'a;

    fn make<'a, P>() -> Self::Fut<'a, P>;
}

impl<T> AsyncTrait for T {
    type Fut<'a, P> = ()
    where
        P: 'a;

    fn make<'a, P>()
    where
        P: 'a,
    {
    }
}

async fn super_async<'a, T, P>()
where
    T: AsyncTrait + 'a,
    P: 'a,
{
    let _x = T::make::<P>();
    async {}.await
}

Apparently it's a compiler bug and error messages about AsRef is just nonsense whatsoever.
Most likely it's directly related to Lifetime bounds in auto trait impls prevent trait from being implemented on generators · Issue #64552 · rust-lang/rust · GitHub. I even tried to fix this bug at some point, but it's just too complicated and I went nowhere.

I hit this exact bug the first time I tried to mix GAT and async. sigh

3 Likes

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.

  1. You said the code works fine if you remove tokio::spawn, then it must be something around tokio::spawn's where clause.
  2. I tried restrain 'static only and it did not error. And it had error with Send only.
  3. I encountered this problem in a much more direct way in the past, so I know there's bug around GAT + async.
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.