我们如何编写用于检查Serde序列化和反序列化的通用函数? [英] How can we write a generic function for checking Serde serialization and deserialization?

查看:56
本文介绍了我们如何编写用于检查Serde序列化和反序列化的通用函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在一个涉及自定义Serde(1.0)序列化和反序列化方法的项目中,我依靠此测试例程来检查序列化一个对象并返回将产生等效的对象.

In a project where custom Serde (1.0) serialization and deserialization methods are involved, I have relied on this test routine to check whether serializing an object and back would yield an equivalent object.

// let o: T = ...;
let buf: Vec<u8> = to_vec(&o).unwrap();
let o2: T = from_slice(&buf).unwrap();
assert_eq!(o, o2);

执行此内联效果很好.我对可重用性的下一步是为此目的创建一个函数 check_serde .

Doing this inline works pretty well. My next step towards reusability was to make a function check_serde for this purpose.

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

这对于拥有类型非常有效,但不适用于具有生命周期限制的类型(游乐场):

This works well for owning types, but not for types with lifetime bounds (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`

由于我希望函数能够在这些情况下工作(包括带有字符串切片的结构),因此我尝试了具有显式对象反序列化生存期的新版本:

As I wish to make the function work with these cases (including structs with string slices), I attempted a new version with an explicit object deserialization lifetime:

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]

此实现导致另一个问题,它将无法编译(游乐场).

This implementation leads to another issue, and it won't compile (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 | | }
   | |_^

我已经预料到了这一点:这个版本意味着序列化的内容(以及反序列化的对象)与输入对象一样长,这是不正确的.缓冲区只能在函数作用域内生存.

I have already expected this one: this version implies that the serialized content (and so the deserialized object) lives as long as the input object, which is not true. The buffer is only meant to live as long as the function's scope.

我的第三次尝试试图构建原始输入的拥有版本,从而避免了使用具有不同生命周期边界的反序列化对象的问题. ToOwned 特性似乎适合此用例.

My third attempt seeks to build owned versions of the original input, thus evading the issue of having a deserialized object with different lifetime boundaries. The ToOwned trait appears to suit this use case.

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

这使该函数现在适用于纯字符串切片,但不适用于包含它们的复合对象(游乐场):

This makes the function work for plain string slices now, but not for composite objects containing them (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]

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

Again, we stumble upon the same error kind as the first version:

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(稳定或每晚)中执行此功能,如果是,那么我的实现缺少哪些调整?

Granted, I'm at a loss. How can we build a generic function that, using Serde, serializes an object and deserializes it back into a new object? In particular, can this function be made in Rust (stable or nightly), and if so, what adjustments to my implementation are missing?

推荐答案

不幸的是,您需要的是Rust中尚未实现的功能:通用关联类型.

Unfortunately, what you need is a feature that is not yet implemented in Rust: generic associated types.

让我们看看 check_serde 的另一种变体:

Let's look at a different variant of 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 ,而没有附加特定寿命.

The problem here is that o2 cannot be of type T: o2 refers to buf, which is a local variable, but type parameters cannot be inferred to types constrained by a lifetime that is restricted to the function's body. We'd like for T to be something like &str without a specific lifetime attached to it.

使用通用关联类型,可以通过类似以下方式解决此问题(显然,由于尚未实现,因此我无法对其进行测试):

With generic associated types, this could be solved with something like this (obviously I can't test it, since it's not implemented yet):

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

这篇关于我们如何编写用于检查Serde序列化和反序列化的通用函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆