【问题标题】:Lifetime issue while implementing a trait for a generic type which implements a trait with an associated lifetime为实现具有关联生命周期的特征的泛型类型实现特征时的生命周期问题
【发布时间】:2021-05-16 19:32:25
【问题描述】:

我正在为一个项目编写一个反序列化器(Vec<u8>(Raw JSON)到任何类型的 T)并尝试使用 serde。这是我解码数据的特征:

pub trait Decode: Sized {
    fn try_decode(input: Vec<u8>) -> Result<Self, InternalError>;
}

因为我打算在我的结构上使用#[derive(Deserialize)],所以我正在为任何实现具有任意生命周期的 Deserialize 特征的类型编写一个 impl:

impl<'a, T: Deserialize<'a>> Decode for T {
    fn try_decode(input: Vec<u8>) -> Result<Self, InternalError> {
        if let Ok(t) = serde_json::from_slice(&input) {
            Ok(t)
        } else {
            Err(Internal::decode("Could not decode"))
        }
    }
}

但我遇到了终身错误:

error[E0597]: `input` does not live long enough
  --> src/decode.rs:16:47
   |
14 | impl<'a, T: Deserialize<'a>> Decode for T {
   |      -- lifetime `'a` defined here
15 |     fn try_decode(input: Vec<u8>) -> Result<Self, InternalError> {
16 |         if let Ok(t) = serde_json::from_slice(&input) {
   |                        -----------------------^^^^^^-
   |                        |                      |
   |                        |                      borrowed value does not live long enough
   |                        argument requires that `input` is borrowed for `'a`
...
21 |     }
   |     - `input` dropped here while still borrowed

我明白编译器为什么生我的气了。在检查 from_slice 的实现后,我发现它有一个关联的生命周期。我确信输入是一个拥有的值,但 from_slice 需要一个引用,所以在这里我返回一个值的引用,该值在函数结束时被删除,这显然是不正确的。我的问题是,有没有办法确保在 生命周期内借用该值(即此函数的调用者提供的生命周期)。我无法更新特征,因为它会导致灾难。

【问题讨论】:

  • 如果你想要一个不从切片中借用的值,你可以使用DeserializeOwned 作为特征绑定,它没有关联的生命周期(因此只存储拥有的值)。例如,this compiles
  • 是的,在其他渠道找到了这个解决方案,谢谢!回答我自己的问题。

标签: rust json-deserialization lifetime serde


【解决方案1】:

如果您想要一个不从切片中借用的值,您可以使用DeserializeOwned 作为特征绑定,它没有关联的生命周期(因此只存储拥有的值)。例如,这样编译:

impl<T: DeserializeOwned> Decode for T {
    fn try_decode(input: Vec<u8>) -> Result<Self, InternalError> {
        if let Ok(t) = serde_json::from_slice(&input) {
            Ok(t)
        } else {
            todo!()
        }
    }
}

Playground

这等价于使用等级较高的 trait bound 与较低级别的 Deserialize trait bound:

impl<T: for<'de> Deserialize<'de>> Decode for T {
    // ...
}

【讨论】:

  • 感谢您的解释和其他解决方案! :)
【解决方案2】:

使用带有生命周期的 Deserialize 的正确方法是使用 Higher Rank Trait Bounds:使用 for&lt;'a&gt; 表示法专门告诉编译器此生命周期对所有(相关)生命周期都有效。代码将如下所示:

impl<T: for<'de> Deserialize<'de>> InputCoercible for T {
    // ...trait methods...
}

【讨论】:

  • 如果他的回答对您有所帮助,请mark it as accepted 而不是单独发布“谢谢”回答,以便人们知道该问题已得到解答。
  • 请注意,此处显示的 HRTB 和 DeserializeOwned 同样“正确”并且实际上是等价的 (docs)。 DeserializeOwned 是精确引入的,因此您不必在每次需要指定 Deserialize 的任何生命周期时都拼出更高级别的 trait bound。
  • 啊,好吧!非常感谢!虽然您确切解释了它们在功能上的相似之处,但我曾询问过如何使用反序列化以及如何验证借用的寿命。您的回答确实对我有所帮助,但我相信 HRTB 的知识是关键。如果您将 HRTB 和 DeserializeOwned 添加到您的答案中,我将非常感激,以便我可以再次将其标记为已接受。 :)
  • 您好,我还没有看到您的评论(直到现在),因为它不包含用于触发通知的 @... 标记。我现在已按要求编辑了答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-05
  • 2019-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多