E_n*_*ate 12 serialization lifetime rust serde
在涉及自定义Serde(1.0)序列化和反序列化方法的项目中,我依靠此测试例程来检查序列化对象和返回是否会产生等效对象.
// let o: T = ...;
let buf: Vec<u8> = to_vec(&o).unwrap();
let o2: T = from_slice(&buf).unwrap();
assert_eq!(o, o2);
Run Code Online (Sandbox Code Playgroud)
这样的内联工作非常好.我对可重用性的下一步是check_serde为此目的发挥作用.
pub fn check_serde<T>(o: T)
where
T: Debug + PartialEq<T> + Serialize + DeserializeOwned,
{
let buf: Vec<u8> = to_vec(&o).unwrap();
let o2: T = from_slice(&buf).unwrap();
assert_eq!(o, o2);
}
Run Code Online (Sandbox Code Playgroud)
这适用于拥有类型,但不适用于具有生命周期边界的类型(Playground):
check_serde(5);
check_serde(vec![1, 2, 5]);
check_serde("five".to_string());
check_serde("wait"); // [E0279]
Run Code Online (Sandbox Code Playgroud)
错误:
error[E0279]: the requirement `for<'de> 'de : ` is not satisfied (`expected bound lifetime parameter 'de, found concrete lifetime`)
--> src/main.rs:24:5
|
24 | check_serde("wait"); // [E0277]
| ^^^^^^^^^^^
|
= note: required because of the requirements on the impl of `for<'de> serde::Deserialize<'de>` for `&str`
= note: required because of the requirements on the impl of `serde::de::DeserializeOwned` for `&str`
= note: required by `check_serde`
Run Code Online (Sandbox Code Playgroud)
由于我希望使函数适用于这些情况(包括带有字符串切片的结构),我尝试了一个具有显式对象反序列化生命周期的新版本:
pub fn check_serde<'a, T>(o: &'a T)
where
T: Debug + PartialEq<T> + Serialize + Deserialize<'a>,
{
let buf: Vec<u8> = to_vec(o).unwrap();
let o2: T = from_slice(&buf).unwrap();
assert_eq!(o, &o2);
}
check_serde(&5);
check_serde(&vec![1, 2, 5]);
check_serde(&"five".to_string());
check_serde(&"wait"); // [E0405]
Run Code Online (Sandbox Code Playgroud)
此实现导致另一个问题,它将无法编译(Playground).
error[E0597]: `buf` does not live long enough
--> src/main.rs:14:29
|
14 | let o2: T = from_slice(&buf).unwrap();
| ^^^ does not live long enough
15 | assert_eq!(o, &o2);
16 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 10:1...
--> src/main.rs:10:1
|
10 | / pub fn check_serde<'a, T>(o: &'a T)
11 | | where T: Debug + PartialEq<T> + Serialize + Deserialize<'a>
12 | | {
13 | | let buf: Vec<u8> = to_vec(o).unwrap();
14 | | let o2: T = from_slice(&buf).unwrap();
15 | | assert_eq!(o, &o2);
16 | | }
| |_^
Run Code Online (Sandbox Code Playgroud)
我已经预料到了这个:这个版本意味着序列化的内容(以及反序列化的对象)和输入对象一样长,这是不正确的.缓冲区只能在函数范围内存活.
我的第三次尝试旨在构建原始输入的自有版本,从而避免了具有不同生命周期边界的反序列化对象的问题.这个ToOwned特性似乎适合这个用例.
pub fn check_serde<'a, T: ?Sized>(o: &'a T)
where
T: Debug + ToOwned + PartialEq<<T as ToOwned>::Owned> + Serialize,
<T as ToOwned>::Owned: Debug + DeserializeOwned,
{
let buf: Vec<u8> = to_vec(&o).unwrap();
let o2: T::Owned = from_slice(&buf).unwrap();
assert_eq!(o, &o2);
}
Run Code Online (Sandbox Code Playgroud)
这使得该函数现在适用于纯字符串切片,但不适用于包含它们的复合对象(Playground):
check_serde(&5);
check_serde(&vec![1, 2, 5]);
check_serde(&"five".to_string());
check_serde("wait");
check_serde(&("There's more!", 36)); // [E0279]
Run Code Online (Sandbox Code Playgroud)
同样,我们偶然发现与第一个版本相同的错误类型:
error[E0279]: the requirement `for<'de> 'de : ` is not satisfied (`expected bound lifetime parameter 'de, found concrete lifetime`)
--> src/main.rs:25:5
|
25 | check_serde(&("There's more!", 36)); // [E0279]
| ^^^^^^^^^^^
|
= note: required because of the requirements on the impl of `for<'de> serde::Deserialize<'de>` for `&str`
= note: required because of the requirements on the impl of `for<'de> serde::Deserialize<'de>` for `(&str, {integer})`
= note: required because of the requirements on the impl of `serde::de::DeserializeOwned` for `(&str, {integer})`
= note: required by `check_serde`
Run Code Online (Sandbox Code Playgroud)
当然,我很茫然.我们如何构建一个泛型函数,使用Serde序列化一个对象并将其反序列化为一个新对象?特别是,这个功能可以在Rust中生成(稳定或每晚),如果是这样,我的实现有哪些调整缺失?
不幸的是,您需要的是一个尚未在Rust中实现的功能:通用关联类型.
让我们看一下不同的变体check_serde:
pub fn check_serde<T>(o: T)
where
for<'a> T: Debug + PartialEq<T> + Serialize + Deserialize<'a>,
{
let buf: Vec<u8> = to_vec(&o).unwrap();
let o2: T = from_slice(&buf).unwrap();
assert_eq!(o, o2);
}
fn main() {
check_serde("wait"); // [E0279]
}
Run Code Online (Sandbox Code Playgroud)
这里的问题是o2不能是类型T:o2引用buf,它是一个局部变量,但是类型参数不能推断为受限于函数体的生命周期约束的类型.我们希望T成为&str 没有特定生命周期的东西.
使用通用关联类型,这可以用这样的东西来解决(显然我无法测试它,因为它还没有实现):
trait SerdeFamily {
type Member<'a>: Debug + PartialEq<Self> + Serialize + Deserialize<'a>;
}
struct I32Family;
struct StrFamily;
impl SerdeFamily for I32Family {
type Member<'a> = i32; // we can ignore parameters
}
impl SerdeFamily for StrFamily {
type Member<'a> = &'a str;
}
pub fn check_serde<'a, Family>(o: Family::Member<'a>)
where
Family: SerdeFamily,
{
let buf: Vec<u8> = to_vec(&o).unwrap();
// `o2` is of type `Family::Member<'b>`
// with a lifetime 'b different from 'a
let o2: Family::Member = from_slice(&buf).unwrap();
assert_eq!(o, o2);
}
fn main() {
check_serde::<I32Family>(5);
check_serde::<StrFamily>("wait");
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
625 次 |
| 最近记录: |