混淆使用具有生命周期的特征作为通用参数约束

swi*_*ard 2 rust

我试图制作某种解码器,只需将值映射到某些内存区域,就可以在不实际复制内存的情况下对条目进行反序列化.这就是我目前设法做的事情(简化为testcase):

#![allow(unstable)]

trait CastAbility: Sized { }
impl CastAbility for u64 { }
impl CastAbility for u32 { }
impl CastAbility for u16 { }
impl CastAbility for u8 { }

trait Cast {
    fn cast<'a>(mem: &'a [u8]) -> Result<&'a Self, String>;
}

impl<T> Cast for T where T: CastAbility {
    fn cast<'a>(mem: &'a [u8]) -> Result<&'a T, String> {
        if mem.len() != std::mem::size_of::<T>() { 
            Err("invalid size".to_string())
        } else {
            Ok(unsafe { std::mem::transmute(mem.as_ptr()) })
        }
    }
}

impl Cast for str {
    fn cast<'a>(mem: &'a [u8]) -> Result<&'a str, String> {
        Ok(unsafe { std::mem::transmute(std::raw::Slice { data: mem.as_ptr(), len: mem.len() }) })
    }
}

trait Read<'a> {
    fn read(mem: &'a [u8]) -> Result<Self, String>;
}

#[derive(Show, PartialEq)]
struct U8AndStr<'a> {
    value_u8: &'a u8,
    value_str: &'a str,
}

impl<'a> Read<'a> for U8AndStr<'a> {
    fn read(mem: &'a [u8]) -> Result<U8AndStr, String> {
        Ok(U8AndStr {
            value_u8: try!(Cast::cast(mem.slice(0, 1))),
            value_str: try!(Cast::cast(mem.slice(1, mem.len()))),
        })
    }
}

fn main() {
    let mem: &[u8] = &[0x01, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37];
    let value: U8AndStr = Read::read(mem).unwrap();

    println!("value: {:?}", value);
}
Run Code Online (Sandbox Code Playgroud)

围栏

实际上它编译甚至可以工作,但现在我无法理解如何使用我的Read特性作为通用参数.例如,假设我想将值与某些内存区域的解码结果进行比较:

fn compare_to_smth<'a, T>(value: &'a T) -> bool where T: PartialEq+Read<'a> {
    let mem: &[u8] = &[0x01, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37];
    let smth_value: T = Read::read(mem).unwrap();
    smth_value == *value
}

fn main() {
    let value = U8AndStr { value_u8: &1, value_str: "01234567" };
    assert!(compare_to_smth(&value));
}
Run Code Online (Sandbox Code Playgroud)

它失败了"借来的价值不够长",我可以猜到为什么:因为mem生命周期是函数体,而不是'a,就像我在输入参数的签名中指定的那样.所以我尝试使用第二次生命参数,如图所示:

fn compare_to_smth<'a, 'b, T>(value: &'a T) -> bool where T: PartialEq+Read<'b> {
Run Code Online (Sandbox Code Playgroud)

但它也没有明显的原因.所以我真的不明白如何在不从外部传递内存块的情况下使compare_to_smth工作.有什么解决方案,或者我应该以某种方式重构代码?

Vla*_*eev 5

不幸的是,你想要做的事情目前在Rust中难以形容.

Read实际可行的特征签名如下(在伪Rust中):

trait<'r> Read for Self<'r> {
    fn read<'a>(mem: &'a [u8]) -> Result<Self<'a>, String>;  // '
}
Run Code Online (Sandbox Code Playgroud)

也就是说,Self它的生命周期参数必须是更高级的类型.这需要支持更高级别的类型,这是Rust社区中非常期望的功能,但仍有待实施.

原始签名的问题:

trait Read<'a> {
    fn read(mem: &'a [u8]) -> Result<Self, String>;
}
Run Code Online (Sandbox Code Playgroud)

'a是特质的参数.当此特征用作特征限制时:

fn compare_to_smth<'a, T>(value: &T) -> bool where T: PartialEq+Read<'a>
Run Code Online (Sandbox Code Playgroud)

这意味着该函数的调用者选择实际的生命周期参数.例如,呼叫者可以选择'static:

fn compare_to_smth<T>(value: &T) -> bool where T: PartialEq+Read<'static>
Run Code Online (Sandbox Code Playgroud)

但是,该函数使用&[u8]的是其生命周期不是'static.

事实上,由于方差,这个具体的例子可能并不完全正确(我想这一辈子的声音会'static在这里,但生命本身的变化本身有些令人困惑,所以我不太确定),但是一般想法是一样的:为了使这个工作,该Read::read方法必须在其参数和结果的生命周期中是多态的,但你还不能写这样的签名.