[英]Confused about using trait with lifetime as generic parameter constraint
我正在嘗試制作某種解碼器,僅通過將值映射到某些內存區域就可以反序列化條目而無需實際復制內存。 這是我目前設法做的(簡化為測試用例):
#![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);
}
實際上,它可以編譯甚至可以工作,但是現在我不明白如何將我的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));
}
它失敗並顯示“借入的值壽命不足”,我可以猜測原因:因為mem生命周期是函數主體,而不是'a ,正如我在簽名中為輸入參數指定的那樣。 所以我嘗試使用第二生命周期參數,如下所示:
fn compare_to_smth<'a, 'b, T>(value: &'a T) -> bool where T: PartialEq+Read<'b> {
但這也沒有明顯的原因。 所以我真的不理解如何在不從外部傳遞內存塊的情況下使compare_to_smth工作。 有什么解決方案,還是我應該以某種方式重構代碼?
不幸的是,目前您想要做的事在Rust中是無法表達的。
實際可用的Read
特性的簽名如下(在偽Rust中):
trait<'r> Read for Self<'r> {
fn read<'a>(mem: &'a [u8]) -> Result<Self<'a>, String>; // '
}
也就是說, Self
必須在其生存期參數中使用更高種類的類型。 這就需要支持更高種類的類型,這在Rust社區中是非常需要的功能,但尚未實現。
原始簽名的問題:
trait Read<'a> {
fn read(mem: &'a [u8]) -> Result<Self, String>;
}
是'a
是特征的參數。 當將此特征用作特征綁定時:
fn compare_to_smth<'a, T>(value: &T) -> bool where T: PartialEq+Read<'a>
這意味着此函數的調用者選擇實際的生存期參數。 例如,呼叫者可以選擇'static
:
fn compare_to_smth<T>(value: &T) -> bool where T: PartialEq+Read<'static>
但是,該函數使用&[u8]
其生存期不是'static
。
實際上,由於偏差,這個具體示例可能並不完全正確(我想這輩子在這里是'static
是合理的,但是壽命本身的偏差在某種程度上令人困惑,因此我不確定)。總體思路是相同的:為了使此方法起作用, Read::read
方法在其參數和結果的生命周期內必須是多態的,但您尚不能編寫此類簽名。
我認為問題可能更多是在compare_to_smth
的簽名中。
fn compare_to_smth<'a, T>(value: &'a T) // this implies a T: 'a bound
// because otherwise we would not be able to
// have a &'a T (references can't live longer
// than the thing they reference)
但是然后在函數內部:
let smth_value: T = Read::read(mem).unwrap();
// give me something of type T that
// lives less than T
我可能是錯的,但是我不認為這取決於Read
的定義方式以及將來類型系統的復雜程度,因為您在右側編寫的任何內容都不會改變您期望的事實。左側的T(並且T必須超過'a
)。
一個“足夠智能的編譯器”也許能夠看到smth_value實際上沒有生存時間長於'a
,並且您正在執行的操作是安全的,但總的來說這是不安全的。 我敢肯定,在compare_to_smth
中使用不安全的轉換會完全違背您的目的,但僅compare_to_smth
演示目的,這是可行的:
fn compare_to_smth<'a, T>(value: &'a T) -> bool
where T: Read<'a> + PartialEq
{
let mem: &[u8] = &[0x01, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37];
let smth_value = <U8AndStr as Read>::read(mem).unwrap();
let vl: &U8AndStr = unsafe{ std::mem::transmute(value) };
smth_value == *vl
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.