Ireina
Ireina

Reputation: 386

Implementation of `AsRef` is not general enough

I'm new to rust's async programming and encountered something weird. I want to introduce async in traits without boxing overhead. I'm using nightly with the #![feature(type_alias_impl_trait)] feature.

Here's minimized code:

#![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?

Upvotes: 0

Views: 442

Answers (1)

Rubens Brand&#227;o
Rubens Brand&#227;o

Reputation: 421

I was able to implement this:

#![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 + ?Sized;
    
    fn test_async<'a, P: AsRef<Path> + ?Sized>(&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>(&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;
            ()
        }
    }
}

I changed the definition a little bit, now lifetime 'a is associated to self and P, also changing P into a reference, so we can also associate his and the AsRef lifetimes to to 'a.

By changing P into a reference, we enforce that we are not consuming it, and the data that this reference points to will live longer then the future returned. Maybe there is a way to receive a owned P value and ensure it's lifetime, but I couldn't figure that out.

The ?Sized was added because P solves into the str type (not &str) that is unsized.

Upvotes: 1

Related Questions