使用自定义数据类型反序列化结构
Deserializing a struct with custom data types
我正在尝试 #[derive(Deserialize, Serialize)]
一些涉及其他自定义结构的结构,因此我可以将它们转换成 JSON,例如:
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
pub struct Exercise {
#[serde(borrow)]
pub name: &'static str,
pub muscle_sub_groups: [MuscleSubGroup; 2],
pub recommended_rep_range: [u32; 2],
pub equipment: EquipmentType,
}
#[derive(Debug, Clone, Deserialize)]
pub struct SetEntry {
pub exercise: Exercise,
pub reps: u32,
pub weight: Weight, // another struct with two: &'static str
pub reps_in_reserve: f32,
}
…但我 运行 喜欢这个:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter
'de due to conflicting requirements
我在网上尝试了多种不同的解决方案,包括定义生命周期,我几乎都成功了。
我所有的代码都是here (sorry for spaghetti). The file in question is exercises.rs。
产生相同错误的最小化示例:
use serde::Deserialize;
#[derive(Deserialize)]
pub struct Inner {
#[serde(borrow)]
pub name: &'static str,
}
#[derive(Deserialize)]
pub struct Outer {
pub inner: Inner,
}
要了解问题所在,让我们看一下展开后的代码。它相当大而且可读性不强,但即使是签名也能提供帮助:
impl Deserialize<'static> for Inner {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'static>,
{
todo!()
}
}
impl<'de> Deserialize<'de> for Outer {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
todo!()
}
}
可以看到,Inner
结构体(原代码中的Exersize
)只有在反序列化器(即源数据)为'static
时才能反序列化,但是Outer
结构(原始代码中的 SetEntry
)为每个反序列化器生命周期实现了反序列化——本质上,它实现了 DeserializeOwned
,即它不需要从任何地方借用它的数据。
现在你可能会问,为什么要这样限制?为什么 Deserializer<'static>
在第一种情况下?答案是 - 当 serde(borrow)
超过 &'static str
.
时,你要求这样做
当反序列化包含引用的结构时,Serde 必须证明这些引用永远不会悬空。因此,反序列化数据的生命周期(即 Deserialize
特征的参数)必须与原始数据的生命周期相关联 - 即 Deserializer
的参数。而且,如果结构对其内容的生命周期有任何限制,这些限制会自动转移到 Deserializer
.
在这种情况下,您要求尽可能严格的限制 - 您要求反序列化为 Inner
的数据在程序结束之前可用。然而,对 Outer
没有这样的限制 - 事实上,它可以将 Inner
视为拥有的数据,因为这个结构根本没有任何生命周期参数,所以 Serde 要求最通用的反序列化器可能然后在 Inner
要求它是 'static
.
时窒息
现在,在这种情况下该怎么办?
首先,你绝对不想在任何runtime-generated数据中使用&'static str
。这是字符串文字的类型,即嵌入到可执行文件本身的字符串,而不是 common-case 字符串。
最简单且可能是最正确的方法是将任何 &'static str
替换为拥有的 String
。这将消除对 serde(borrow)
的需要,并使您可以从任何东西构造可反序列化的结构。
但是,如果您想使用引用(例如,消除不必要的副本),您必须将整个结构树视为对反序列化器的临时借用——也就是说,您将生命周期参数绑定到每个直接或间接包含 &str
:
的结构中的 &str
use serde::Deserialize;
#[derive(Deserialize)]
pub struct Inner<'a> {
#[serde(borrow)]
pub name: &'a str,
}
#[derive(Deserialize)]
pub struct Outer<'a> {
#[serde(borrow)]
pub inner: Inner<'a>,
}
然后,当手动创建这些内部有字符串文字的结构时,您只需将 'static
替换为 'a
。
我正在尝试 #[derive(Deserialize, Serialize)]
一些涉及其他自定义结构的结构,因此我可以将它们转换成 JSON,例如:
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
pub struct Exercise {
#[serde(borrow)]
pub name: &'static str,
pub muscle_sub_groups: [MuscleSubGroup; 2],
pub recommended_rep_range: [u32; 2],
pub equipment: EquipmentType,
}
#[derive(Debug, Clone, Deserialize)]
pub struct SetEntry {
pub exercise: Exercise,
pub reps: u32,
pub weight: Weight, // another struct with two: &'static str
pub reps_in_reserve: f32,
}
…但我 运行 喜欢这个:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter
'de due to conflicting requirements
我在网上尝试了多种不同的解决方案,包括定义生命周期,我几乎都成功了。
我所有的代码都是here (sorry for spaghetti). The file in question is exercises.rs。
产生相同错误的最小化示例:
use serde::Deserialize;
#[derive(Deserialize)]
pub struct Inner {
#[serde(borrow)]
pub name: &'static str,
}
#[derive(Deserialize)]
pub struct Outer {
pub inner: Inner,
}
要了解问题所在,让我们看一下展开后的代码。它相当大而且可读性不强,但即使是签名也能提供帮助:
impl Deserialize<'static> for Inner {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'static>,
{
todo!()
}
}
impl<'de> Deserialize<'de> for Outer {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
todo!()
}
}
可以看到,Inner
结构体(原代码中的Exersize
)只有在反序列化器(即源数据)为'static
时才能反序列化,但是Outer
结构(原始代码中的 SetEntry
)为每个反序列化器生命周期实现了反序列化——本质上,它实现了 DeserializeOwned
,即它不需要从任何地方借用它的数据。
现在你可能会问,为什么要这样限制?为什么 Deserializer<'static>
在第一种情况下?答案是 - 当 serde(borrow)
超过 &'static str
.
时,你要求这样做
当反序列化包含引用的结构时,Serde 必须证明这些引用永远不会悬空。因此,反序列化数据的生命周期(即 Deserialize
特征的参数)必须与原始数据的生命周期相关联 - 即 Deserializer
的参数。而且,如果结构对其内容的生命周期有任何限制,这些限制会自动转移到 Deserializer
.
在这种情况下,您要求尽可能严格的限制 - 您要求反序列化为 Inner
的数据在程序结束之前可用。然而,对 Outer
没有这样的限制 - 事实上,它可以将 Inner
视为拥有的数据,因为这个结构根本没有任何生命周期参数,所以 Serde 要求最通用的反序列化器可能然后在 Inner
要求它是 'static
.
现在,在这种情况下该怎么办?
首先,你绝对不想在任何runtime-generated数据中使用&'static str
。这是字符串文字的类型,即嵌入到可执行文件本身的字符串,而不是 common-case 字符串。
最简单且可能是最正确的方法是将任何 &'static str
替换为拥有的 String
。这将消除对 serde(borrow)
的需要,并使您可以从任何东西构造可反序列化的结构。
但是,如果您想使用引用(例如,消除不必要的副本),您必须将整个结构树视为对反序列化器的临时借用——也就是说,您将生命周期参数绑定到每个直接或间接包含 &str
:
&str
use serde::Deserialize;
#[derive(Deserialize)]
pub struct Inner<'a> {
#[serde(borrow)]
pub name: &'a str,
}
#[derive(Deserialize)]
pub struct Outer<'a> {
#[serde(borrow)]
pub inner: Inner<'a>,
}
然后,当手动创建这些内部有字符串文字的结构时,您只需将 'static
替换为 'a
。