rust “AsRef”的实现不够通用

d7v8vwbk  于 2022-12-29  发布在  其他
关注(0)|答案(1)|浏览(112)

我刚开始使用rust的异步编程,遇到了一些奇怪的事情。我想在traits中引入异步而不需要装箱开销。我正在使用nightly和#![feature(type_alias_impl_trait)]特性。
下面是最小化的代码:

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

然后我得到了错误消息:

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`

我不明白错误消息。如果我删除tokio::spawn或仅为String实现SuperAsync,则错误消失。需要帮助吗?

42fyovps

42fyovps1#

我能够实现这一点:

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

我稍微修改了一下定义,现在生存期'aselfP关联,同时将P更改为引用,因此我们还可以将他的和AsRef的生存期与'a关联。
通过将P修改为引用,我们强制我们不使用它,并且这个引用所指向的数据将比将来返回的数据生存更长时间。也许有一种方法可以接收一个拥有的P值并确保它的生存期,但我不知道该方法。
之所以添加?Sized,是因为P解析为未调整大小的str类型(而不是&str)。

相关问题