serialization - 我们如何编写一个通用函数来检查 Serde 序列化和反序列化?

标签 serialization rust lifetime 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);

这样做内联效果很好。为了实现可重用性,我的下一步是为此创建一个函数 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);
}

这适用于拥有类型,但不适用于具有生命周期范围 (Playground) 的类型:

check_serde(5);
check_serde(vec![1, 2, 5]);
check_serde("five".to_string());
check_serde("wait"); // [E0279]

错误:

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`

因为我希望使该函数适用于这些情况(包括带有字符串切片的结构),所以我尝试了一个具有显式对象反序列化生命周期的新版本:

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]

此实现会导致另一个问题,它不会编译 (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 | | }
   | |_^

我已经预料到这个:这个版本意味着序列化内容(以及反序列化对象)与输入对象一样长,但事实并非如此。缓冲区仅在函数范围内有效。

我的第三次尝试旨在构建原始输入的拥有版本,从而避免具有不同生命周期边界的反序列化对象的问题。 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);
}

这使得该函数现在适用于纯字符串切片,但不适用于包含它们的复合对象 (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]

再次,我们偶然发现了与第一个版本相同的错误类型:

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`

当然,我不知所措。我们如何构建一个通用函数,使用 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]
}

这里的问题是o2不能是T类型:o2指的是buf,也就是局部变量,但类型参数不能推断为受限于函数主体的生命周期约束的类型。我们希望 T 类似于 &str 没有附加的特定生命周期。

对于泛型关联类型,这可以通过类似这样的方式解决(显然我无法测试它,因为它尚未实现):

trait SerdeFamily {
    type Member<'a>: Debug + for<'b> PartialEq<Self::Member<'b>> + Serialize + Deserialize<'a>;
}

struct I32Family;
struct StrFamily;

impl SerdeFamily for I32Family {
    type Member<'a> = i32; // ignoring a parameter is allowed
}

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");
}

关于serialization - 我们如何编写一个通用函数来检查 Serde 序列化和反序列化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46514290/

相关文章:

rust - 编译器建议我添加一个 'static lifetime because the parameter type may not live long enough, but I don' t think that's what I want

rust - 更高等级的生命周期和泛型表现不佳

c# - C#中涉及引用时的序列化与反序列化

rust - 'let x = x' 在 Rust 中有什么作用?

Java序列化-将对象写入文件后修改对象

Rust 进程使用 GitHub 操作失败,退出代码为 101

rust - 在遍历对 Result 的引用时如何使用问号运算符?

closures - 通用 fn、 channel 和线程生成

对象的json序列化

python - 在 Django Rest Framework 中序列化字符串而不进行更改?