我正在尝试编写一个用于在进程之间发送消息的通用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
2条答案
按热度按时间kh212irz1#
将泛型类型参数移动到
send_msg
,而不是将trait设为泛型:那么每次调用
send_msg
都可以使用不同的生存期参数,完整的解决方案是here。sbtkgmzw2#
正如您所注意到的,
&T
需要一个生存期才能成为一个完整的类型,而for<'a> &'a T
并不存在,具体的问题是SenderImpl<T>
必须有一个完整的类型T
,并且只能实现具有该类型的Sender
。您可以通过避免在
SenderImpl<T>
中引用(通过生成T = [u8]
)并允许它在任何生存期内实现Sender<&T>
来解决此问题:这种方法在T有自己的寿命参数的情况下似乎不起作用。
你也可以让它通用于任何特定的类型,只要
SenderImpl
不会有一个特定的类型与它相关联,除了trait实现所暗示的:或者,类似于自我解答,您可以为任何类型实现
Sender<T>
,但是像上面一样,SenderImpl
显然不会特定于任何特定类型: