Rust Serde Deserialize 如何标注生命周期 9037 | 特厉害计划
  • Why Github?
  • Team
  • Enterprise
  • Explore
  • Marketplace
  • Pricing
Sign inSign up
Watch996
Star102.4k
Fork61.8k
Tag: learning.rust
Switch branches/tags
Branches
Tags
K / Rust Serde Deserialize 如何标注生命周期.md
120 lines 11.62 KB
First commit on 19 Jun 2020

    用法

    fn do_it<T> (param: T) where for<'de> T: serde::Deserialize<'de> {}
    

    文档:

    文档用的闭包举例。

    https://doc.rust-lang.org/nomicon/hrtb.html

    This job requires The Magic of Higher-Rank Trait Bounds (HRTBs). The way we desugar this is as follows:

    where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8,
    

    (Where Fn(a, b, c) -> d is itself just sugar for the unstable real Fn trait)

    for<'a> can be read as "for all choices of 'a", and basically produces an infinite list of trait bounds that F must satisfy. Intense. There aren't many places outside of the Fn traits where we encounter HRTBs, and even for those we have a nice magic sugar for the common cases.

    帖子

    a newcomer 发表于 2020-03-05 00:52

    Tags:actix_web,serde,lifetime

    我是用actix_web 2.0。 根据官方教程,我使用 serde 解 json。于是有了如下代码:

    async fn deserialize_json<'a, T>(mut payload: web::Payload) -> Result<T, Error>
    where
        T: 'a + serde::Deserialize<'a>,
    {
        const MAX_SIZE: usize = 262_144; // max payload size is 256k
    
        // payload is a stream of Bytes objects
        let mut body:BytesMut= BytesMut::new();
        while let Some(chunk) = payload.next().await {
            let chunk = chunk?;
            // limit max size of in-memory payload
            if (body.len() + chunk.len()) > MAX_SIZE {
                return Err(error::ErrorBadRequest("overflow"));
            }
            body.extend_from_slice(&chunk);
        }
    
        // body is loaded, now we can deserialize serde-json
        let obj = serde_json::from_slice::<T>(&body)?;
        Ok(obj)
    }
    

    然后编译器报错:body does not live long enough 具体如下

       |
    45 | async fn deserialize_json<'a, T>(mut payload: web::Payload) -> Result<T, Error>
       |                           -- lifetime `'a` defined here
    ...
    63 |     let obj:T = serde_json::from_slice::<T>(&body)?;
       |                 ----------------------------^^^^^-
       |                 |                           |
       |                 |                           borrowed value does not live long enough
       |                 argument requires that `body` is borrowed for `'a`
    64 |     Ok(obj)
    65 | }
       | - `body` dropped here while still borrowed
    

    然后,我分析了一波。我把T换成一个确定的结构体,然后编译通过了,并且跑的结果也正确。具体结构体如下:

    #[derive(Serialize, Deserialize, Debug)]
    pub struct LoginStruct {
        username: String,
        password: String,
    }
    

    然后我就没有思路了,搜了半天生命周期和泛型,毫无头绪。

    最后我加了一个参数,从外部传入一个 BytesMut 来做 body 的owner,然而编译器的报错一个字都没变。改后具体代码如下:

    async fn deserialize_json<'a, T>(mut payload: web::Payload,  buffer: &'a mut BytesMut) -> Result<T, Error>
    where
        T: serde::Deserialize<'a>,
    {
        const MAX_SIZE: usize = 262_144; // max payload size is 256k
    
        // payload is a stream of Bytes objects
        let mut body = buffer;
        while let Some(chunk) = payload.next().await {
            let chunk = chunk?;
            // limit max size of in-memory payload
            if (body.len() + chunk.len()) > MAX_SIZE {
                return Err(error::ErrorBadRequest("overflow"));
            }
            body.extend_from_slice(&chunk);
        }
    
        // body is loaded, now we can deserialize serde-json
        let obj = serde_json::from_slice::<T>(&body)?;
        Ok(obj)
    }
    

    请教各位大神,如何才能延长body的生命周期,使函数编译通过?

    刚刚开始学rust,谢谢大家

    评论区写评论

    solarsail 2020-04-18 17:50

    1. serde::Deserialize<'de> 带生命周期参数的目的是允许用户实现“零拷贝反序列化”,在这个例子中就是 serde_json::from_slice::<T>(&body) 中的 T 可以没有自己的数据,所有成员(或者部分成员)都引用 body 的内容,这样就使得 T 绑定了生命周期。
    2. 当 T 的 trait bound 为 'a + serde::Deserialize<'a> 的时候,编译器不能确定 T 中有没有 body 的引用,只能按有对待,所以需要 T 的生命周期不超出 body。这就解释了错误提示“argument requires that body is borrowed for 'a”,因为 T: 'a,所以至少需要 &'a body 成立,但是 obj 是要返回到函数外面的,而 body 只能活在函数内部,所以必然不能满足,从而有“body dropped here while still borrowed”。
    3. 当把 T 换成所述的结构体后,编译器确定 T 是不需要引用 body 的(因为成员都是 String),所以不再要求生命周期一致。
    4. 使用 for<'a > serde::Deserialize<'a> 或者 serde::de::DeserializeOwned 作为 T 的 trait bound,是明确告知编译器 T 不引用 body 的内容,所以编译器不会提生命周期的要求。但如果调用该函数的时候使用了引用 body 的 T,就会使上述 bound 不满足而无法通过编译。

    PS. serde::de::DeserializeOwned 的定义:

    impl<T> DeserializeOwned for T where
        T: for<'de> Deserialize<'de>,
    

    作者 a newcomer 2020-03-05 14:11

    感谢!您的方法确实有效

    对以下内容的回复:

    Dengjianping 2020-03-05 13:59

    两种办法,参考serde反序列化的用法。https://serde.rs/lifetimes.html

    1. HRTB. 借用
    async fn deserialize_json<T>(mut payload: web::Payload) -> Result<T, Error>
    where T: for<'a >+ serde::Deserialize<'a>
    

    转移所有权.

    async fn deserialize_json<T>(mut payload: web::Payload) -> Result<T, Error>
    where T: serde::de::DeserializeOwned
    

    没编译试过,不过应该可以。

    shaitao 2020-03-05 13:38

    我知道了, 你给T加一个'static试试 对以下内容的回复:

    作者 a newcomer 2020-03-05 12:19

    为什麽把 T 换成 LoginStruct 就通过编译了呢?

    对以下内容的回复:

    shaitao 2020-03-05 10:24

    你这里,, aysnc不是立马执行的, async的结果可能还会跨线程,不要把 &mut buffer传进来, 用let body = BytesMut::new(), 要传的话..最简单的用Arc<Muxtex<>>

    原文链接:https://rustcc.cn/article?id=03fbdc76-ebcd-45a9-9843-b43264b03dfd