rust 返回具有更高等级特性界限的抽象类型时出现问题

rta7y2nd  于 2023-01-09  发布在  其他
关注(0)|答案(2)|浏览(100)

我正在尝试编写一个用于在进程之间发送消息的通用trait,其简化版本如下所示:

trait Sender<T> {
    fn send_msg(&mut self, to: u64, body: T) -> Result<()>;
}

我希望能够发送包含引用的消息,以避免复制数据:

#[derive(Serialize)]
enum MsgBody<'a> {
    Success,
    Fail(Error),
    Write { offset: u64, buf: &'a [u8] },
}

但是我遇到了一个问题,类型参数T只能引用一个生存期,阻止了任何生存期的引用。为了说明这一点,请考虑Sender<T>的伪实现:

struct SenderImpl<T>(PhantomData<T>);

impl<T: Serialize> Sender<T> for SenderImpl<T> {
    fn send_msg(&mut self, to: u64, body: T) -> Result<()> {
        Ok(())
    }
}

现在如果我尝试返回一个对任何生存期参数都有效的发送方

fn ref_sender() -> impl for<'a> Sender<MsgBody<'a>> {
    SenderImpl(PhantomData)
}

然后我得到一个编译错误,因为类型参数T不是在所有可能的生存期内通用的:

error: implementation of `Sender` is not general enough
  --> src/lib.rs:29:5
   |
29 |     SenderImpl(PhantomData)
   |     ^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Sender` is not general enough
   |
   = note: `SenderImpl<MsgBody<'2>>` must implement `Sender<MsgBody<'1>>`, for any lifetime `'1`...
   = note: ...but it actually implements `Sender<MsgBody<'2>>`, for some specific lifetime `'2`

这个错误是有道理的,但我不知道如何表达我想要的。我想说的是

fn ref_sender() -> impl for<'a> Sender<MsgBody<'a>> {
    SenderImpl(PhantomData::<for<'a> MsgBody<'a>>)
}

但是当然for<'a> MsgBody<'a>实际上不是类型。
我可以通过在Sender trait中使用MsgBody来解决这个问题,而不是使其通用(您可以看到here),但我不喜欢这个解决方案,因为它牺牲了灵活性。
有没有办法让泛型Sender<T>处理具有生存期参数的类型?
编辑:下面是这个问题中使用的代码:https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=0aac9134639e9593698bfe9da7722d9f

kh212irz

kh212irz1#

将泛型类型参数移动到send_msg,而不是将trait设为泛型:

trait Sender {
    fn send_msg<T: Serialize>(&mut self, to: u64, body: T) -> Result<()>;
}

那么每次调用send_msg都可以使用不同的生存期参数,完整的解决方案是here

sbtkgmzw

sbtkgmzw2#

正如您所注意到的,&T需要一个生存期才能成为一个完整的类型,而for<'a> &'a T并不存在,具体的问题是SenderImpl<T>必须有一个完整的类型T,并且只能实现具有该类型的Sender
您可以通过避免在SenderImpl<T>中引用(通过生成T = [u8])并允许它在任何生存期内实现Sender<&T>来解决此问题:

struct SenderImpl<T: ?Sized>(PhantomData<T>);

impl<'a, T: Serialize + ?Sized> Sender<&'a T> for SenderImpl<T> {
    fn send_msg(&mut self, to: u64, body: &'a T) -> Result<()> {
        Ok(())
    }
}
    • 编辑:**

这种方法在T有自己的寿命参数的情况下似乎不起作用。
你也可以让它通用于任何特定的类型,只要SenderImpl不会有一个特定的类型与它相关联,除了trait实现所暗示的:

struct SenderImpl;

impl<'a> Sender<MsgBody<'a>> for SenderImpl {
    fn send_msg(&mut self, to: u64, body: MsgBody<'a>) -> Result<()> {
        Ok(())
    }
}

fn ref_sender() -> impl for<'a> Sender<MsgBody<'a>> {
    SenderImpl
}

或者,类似于自我解答,您可以为任何类型实现Sender<T>,但是像上面一样,SenderImpl显然不会特定于任何特定类型:

struct SenderImpl;

impl<T: Serialize> Sender<T> for SenderImpl {
    fn send_msg(&mut self, to: u64, body: T) -> Result<()> {
        Ok(())
    }
}

fn ref_sender() -> impl for<'a> Sender<MsgBody<'a>> {
    SenderImpl
}

相关问题