考虑这个简单的协议实现:
#[derive(PartialEq, Debug)]
enum Req<'a> {
InputData(&'a [u8]),
Stop,
}
impl<'a> Req<'a> {
fn decode(packet: &'a [u8]) -> Result<Req<'a>, String> {
match packet.first() {
Some(&0x01) => Ok(Req::InputData(&packet[1..])),
Some(&0x02) => Ok(Req::Stop),
_ => Err(format!("invalid request: {:?}", packet)),
}
}
}
#[derive(PartialEq, Debug)]
enum Rep<'a> {
OutputData(&'a [u8]),
StopAck,
}
impl<'a> Rep<'a> {
fn decode(packet: &'a [u8]) -> Result<Rep<'a>, String> {
match packet.first() {
Some(&0x01) => Ok(Rep::OutputData(&packet[1..])),
Some(&0x02) => Ok(Rep::StopAck),
_ => Err(format!("invalid reply: {:?}", packet)),
}
}
}
fn assert_req(packet: Vec<u8>, sample: Req) {
assert_eq!(Req::decode(&packet), Ok(sample));
}
fn assert_rep(packet: Vec<u8>, sample: Rep) {
assert_eq!(Rep::decode(&packet), Ok(sample));
}
fn main() {
assert_req(vec![1, 2, 3], Req::InputData(&[2, 3]));
assert_req(vec![2], Req::Stop);
assert_rep(vec![1, 2, 3], Rep::OutputData(&[2, 3]));
assert_rep(vec![2], Rep::StopAck);
}
这样可行,但两个函数assert_req
和assert_rep
具有相同的代码,只有类型的差异。编写一个通用assert_packet
:
trait Decode<'a>: Sized {
fn decode(packet: &'a [u8]) -> Result<Self, String>;
}
#[derive(PartialEq, Debug)]
enum Req<'a> {
InputData(&'a [u8]),
Stop,
}
impl<'a> Decode<'a> for Req<'a> {
fn decode(packet: &'a [u8]) -> Result<Req<'a>, String> {
match packet.first() {
Some(&0x01) => Ok(Req::InputData(&packet[1..])),
Some(&0x02) => Ok(Req::Stop),
_ => Err(format!("invalid request: {:?}", packet)),
}
}
}
#[derive(PartialEq, Debug)]
enum Rep<'a> {
OutputData(&'a [u8]),
StopAck,
}
impl<'a> Decode<'a> for Rep<'a> {
fn decode(packet: &'a [u8]) -> Result<Rep<'a>, String> {
match packet.first() {
Some(&0x01) => Ok(Rep::OutputData(&packet[1..])),
Some(&0x02) => Ok(Rep::StopAck),
_ => Err(format!("invalid reply: {:?}", packet)),
}
}
}
fn assert_packet<'a, T>(packet: Vec<u8>, sample: T)
where
T: Decode<'a> + PartialEq + std::fmt::Debug,
{
assert_eq!(T::decode(&packet), Ok(sample));
}
fn main() {
assert_packet(vec![1, 2, 3], Req::InputData(&[2, 3]));
assert_packet(vec![2], Req::Stop);
assert_packet(vec![1, 2, 3], Rep::OutputData(&[2, 3]));
assert_packet(vec![2], Rep::StopAck);
}
然而,这会触发“活不够久”的错误:
error[E0597]: `packet` does not live long enough
--> src/main.rs:41:27
|
41 | assert_eq!(T::decode(&packet), Ok(sample));
| ^^^^^^ does not live long enough
42 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 37:1...
--> src/main.rs:37:1
|
37 | / fn assert_packet<'a, T>(packet: Vec<u8>, sample: T)
38 | | where
39 | | T: Decode<'a> + PartialEq + std::fmt::Debug,
40 | | {
41 | | assert_eq!(T::decode(&packet), Ok(sample));
42 | | }
| |_^
如果我理解正确,问题出在函数签名中:
fn assert_packet<'a, T>(packet: Vec<u8>, sample: T)
where
T: Decode<'a>
此处,函数返回时会销毁packet
,但用户提供的'a
生命周期参数表示生命周期应该在assert_packet
函数之外的某处结束。有没有正确的解决方案?签名应该怎么样?也许higher rank trait bounds可以帮到这里吗?
答案 0 :(得分:3)
为什么要编译:
fn assert_req(packet: Vec<u8>, sample: Req) {
assert_eq!(Req::decode(&packet), Ok(sample));
}
虽然这不是?
fn assert_packet<'a, T>(packet: Vec<u8>, sample: T) where T: Decode<'a> + PartialEq + std::fmt::Debug {
assert_eq!(T::decode(&packet), Ok(sample));
}
不同之处在于,在第一个版本中,Req
的两个文本事件命名了Req<'a>
结构的两个不同实例,具有两个不同的生命周期。第一次出现在sample
参数上,专门用于assert_req
函数接收的生命周期参数。第二次出现,用于调用decode
,专门用于packet
参数本身的生命周期(一旦函数返回就会停止存在)。这意味着assert_eq!
的两个参数的类型不同;但是,它会进行编译,因为Req<'a>
可以强制转换为Req<'b>
,其中'b
的生命周期比'a
更短(Req<'a>
是Req<'b>
的子类型})。
另一方面,在第二个版本中,T
的出现次数必须代表完全相同的类型。始终假设生命周期参数表示比函数调用更长的生命周期,因此调用T::decode
的生命周期更短是错误的。
这是一个较短的功能,表现出同样的问题:
fn decode_packet<'a, T>(packet: Vec<u8>) where T: Decode<'a> {
T::decode(&packet);
}
此函数无法编译:
<anon>:38:16: 38:22 error: `packet` does not live long enough
<anon>:38 T::decode(&packet);
^~~~~~
可以在此函数上使用更高级别的特征边界来进行编译:
fn decode_packet<T>(packet: Vec<u8>) where for<'a> T: Decode<'a> {
T::decode(&packet);
}
但是,现在我们还有另一个问题:我们无法调用此功能!如果我们尝试像这样调用它:
fn main() {
decode_packet(vec![1, 2, 3]);
}
我们收到此错误:
<anon>:55:5: 55:18 error: unable to infer enough type information about `_`; type annotations or generic parameter binding required [E0282]
<anon>:55 decode_packet(vec![1, 2, 3]);
^~~~~~~~~~~~~
那是因为我们没有指定我们想要使用哪个Decode
实现,并且没有编译器可用于推断此信息的参数。
如果我们指定实施怎么办?
fn main() {
decode_packet::<Req>(vec![1, 2, 3]);
}
我们收到此错误:
<anon>:55:5: 55:25 error: the trait `for<'a> Decode<'a>` is not implemented for the type `Req<'_>` [E0277]
<anon>:55 decode_packet::<Req>(vec![1, 2, 3]);
^~~~~~~~~~~~~~~~~~~~
这不起作用,因为我们写的Req
实际上被解释为Req<'_>
,其中'_
是编译器推断的生命周期。对于所有可能的生命周期,这不会实现Decode
,仅适用于一个特定的生命周期。
为了能够实现您想要的功能,Rust必须支持higher kinded types。然后可以在函数上定义类型构造函数参数(而不是类型参数)。例如,您可以将Req
或Rep
作为需要生命周期参数的类型构造函数传递,以生成特定的Req<'a>
类型。
答案 1 :(得分:0)
如果您将所涉及的生命周期与您引用的输入相关联,它就能正常工作,例如
fn assert_packet<'a, T>(packet: &'a [u8], sample: T) where T: Decode<'a> ..
(您还需要更新测试以通过借用而非拥有Vec
。)