Consider this simple protocol implementation:
#[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); } This works, but the two functions assert_req and assert_rep have identical code with only a difference in types. It is a good idea to write one generic 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); } However, this triggers a "does not live long enough" error:
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 | | } | |_^ If I understand correctly, the problem is in the function signature:
fn assert_packet<'a, T>(packet: Vec<u8>, sample: T) where T: Decode<'a> Here, packet is destroyed when the function returns, but the user-provided 'a lifetime parameter says that the lifetime should end somewhere outside the assert_packet function. Is there any right solution? How should the signature look? Maybe higher rank trait bounds could help here?