Reputation: 4412
I've got code somewhat like the following, which attempts to read from a websocket, parse the JSON result into a struct, and push that struct onto a Vec
buffer. The code however fails to compile, because the struct has a lifetime, and the borrow checker complains that the JSON string does not live long enough.
use serde::{Deserialize, Serialize};
use tungstenite::client::AutoStream;
use tungstenite::protocol::WebSocket;
#[derive(Serialize, Deserialize, Debug, Clone)]
struct MyType<'a> {
id: &'a str,
count: i64,
}
fn example<'a>(
conn: &mut WebSocket<AutoStream>,
buff: &'a mut Vec<MyType<'a>>,
) -> Option<Box<dyn std::error::Error>> {
match conn.read_message() {
Err(err) => Some(err.into()),
Ok(msg) => {
let resp_raw = msg.to_string();
let resp_parsed: Result<MyType<'a>, _> = serde_json::from_str(&resp_raw);
match resp_parsed {
Err(err) => Some(err.into()),
Ok(resp) => {
buff.push(resp.clone());
None
}
}
}
}
}
The exact error is that borrowed value [&resp_raw] does not live long enough
.
I'm wondering how I should restructure this code to satisfy the borrow checker; what is the correct way to push a struct with a lifetime onto the Vec
param?
Or is it the case that the &'a str
parsed into MyType
actually still retains a reference to the original JSON string, so there's no way to safely do this?
Upvotes: 0
Views: 949
Reputation: 16475
Look at serde_json::from_str
carefully:
pub fn from_str<'a, T>(s: &'a str) -> Result<T>
where
T: Deserialize<'a>,
This says that the T
which is deserialized shares the same lifetime as the input s
. This allows for zero-copy deserialization, which is what you get in MyType
, where id
is a reference to a string slice. This binds the lifetime of MyType
to the lifetime of &resp_raw
, which is local to fn example()
. This will not work.
The problem can't be solved by giving buff
the lifetime-parameter you've given it. The example
-function owns the buffer that MyType
point into. Allowing MyType
to "escape" into the Vec
would allow a dangling reference to be created, as the buffer is destroyed once example
returns.
Change MyType
to satisify DeserializeOwned
, that is, take no lifetime parameter. You'll need a String
or a (to safe a little memory) a Box<str>
instead of a &str
.
Upvotes: 1