< 返回版块

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 的内容,所以编译器不会提生命周期的要求。但如果调用该函数的时候使用了引用 bodyT,就会使上述 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>
  1. 转移所有权.
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<>>

1 共 6 条评论, 1 页