在FromForm中反序列化JSON时的生命周期

4
我有些困难理解这段代码中生命周期的关系。基本上,我有一个Rocket API,它接收一些x-www-form-urlencoded数据,只有一个键:json。直观地说,这个键包含一个使用百分号编码的JSON值,该值是一个结构体Message<T>
(我知道这不是最佳的API设计,但这是逆向工程,所以我没有选择)
为了方便地作为请求保护,我正在实现FromForm,并将其用作From<Message<T>>。为此,我需要为任何T实现Deserialize<'de>Message<T>实现FromForm<'f>。我将我的impl签名写成了impl<'f, 'de, T> FromForm<'f> for Message<T> where T: Deserialize<'de>
要执行解码,我需要:
  1. 获取表单数据中的"json"键;
  2. 对值进行URL解码;
  3. 解析值中包含的JSON。
尽快退出。以下是执行此操作的代码(显式类型注释方便读者):
fn from_form(items: &mut FormItems<'f>, strict: bool) -> Result<Self, Self::Error> {
    // Get JSON field
    let encoded: Option<&RawStr> = items.find(|&(k, _)| k.as_str() == "json")
        .map(|(_, v)| v);
    if let None = encoded {
        return Err(MessageFormError::MissingJsonKey);
    }

    // Decode URL-string
    let decoded: Result<String, Utf8Error> = encoded.unwrap().url_decode();
    if let Err(e) = decoded {
        return Err(MessageFormError::InvalidUrl(e));
    }

    // Parse JSON
    let json: String = decoded.unwrap();
    serde_json::from_str::<Self>(&json) // Line 205
        .map_err(|e| MessageFormError::InvalidJson(e))
}
这个Gist演示了问题,可以直接复制运行(在Playground上无法运行,因为它依赖于Rocket)。
根据我的理解:
  • encoded&RawStr的寿命为'f
  • url_decode通过它创建了一个String,该String的寿命一直持续到函数结束。
  • serde_json需要一个&'x str参数,其中的'x'de不必重合,并返回一个值(因此它的寿命延续到函数的结束,并且由于被返回,所以会被移动出去)。
但看起来我的理解是错误的:
205 |         serde_json::from_str::<Self>(&json)
    |                                       ^^^^ does not live long enough
206 |             .map_err(|e| MessageFormError::InvalidJson(e))
207 |     }
    |     - borrowed value only lives until here
    |
note: borrowed value must be valid for the lifetime 'f as defined on the impl at 184:1...
   --> src/transport.rs:184:1
    |
184 | / impl<'f, T> FromForm<'f> for Message<T>
185 | |     where T: Deserialize<'f>
186 | | {
187 | |     type Error = MessageFormError;
...   |
207 | |     }
208 | | }
    | |_^

我哪里出错了,如何正确地返回反序列化的值?

你能把代码放到playground里面吗?这样就可以很容易地复现了。乍一看,'f'de之间似乎缺少关联。 - E net4
@E_net4 不完全正确,因为游乐场没有 Rocket crate,很遗憾。我会制作一个更完整的示例,可以粘贴到文件中。 - Kroltan
@E_net4 添加了代码片段! - Kroltan
1个回答

9

这部分Serde网站详细介绍了Deserialize边界。



有两种主要的方法来编写Deserialize trait bounds,无论是在impl块还是函数或其他任何地方。
1. `< 'de, T > where T: Deserialize< 'de >` 这意味着“T可以从某个生命周期反序列化”。调用者决定生命周期。通常情况下,当调用者也提供正在反序列化的数据时,例如在类似 `serde_json :: from_str` 的函数中时,输入数据也必须具有 生命周期'de' ,例如可能是 `&'de str` 。
2. `< T > where T: DeserializeOwned` 这意味着“T可以从任何生命周期反序列化”。被调用者决定生命周期。通常是因为将从中进行反序列化的数据将在函数返回之前被丢弃,所以不能允许T从中借用。在您的情况下,数据来自URL解码的某些输入,并且在反序列化T之后丢弃解码的数据。此限制的另一个常见用途是从IO流反序列化的函数,例如 `serde_json :: from_reader`。
更技术性地说, `DeserializeOwned` trait 等价于高阶trait bound `for<'de> Deserialize<'de>` 。唯一的区别是 `DeserializeOwned` 更直观易读。它表示T拥有所有被反序列化的数据。
T: DeserializeOwned替换你的T: Deserialize<'f>约束,可以更准确地表明T不允许从URL解码的数据中借用,因为URL解码的数据不会比T存在更久。

啊,现在我明白了。输入必须比输出存在更长的时间,这样从引用本身就可以看出它们的生命周期是如何相互关联的。但我同意Shepmaster的观点,请直接回答问题。 - Kroltan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接