I am refactoring a simple Rust project. Some structures in the project implement a run
method, which all share the same behaviour:
- receive a WebSocket message.
- deserialize the message to a Rust structure (Implement
Deserialize<'de'>
, notDeserializedOwned
for performance consideration) viaserde_json::from_str
. - pass the deserialized structure to a callback function.
Here is a simplified example to demonstrate the implementation currently:
use serde::Deserialize;
fn message() -> String {
r#"{"field1":"value1"}"#.to_string()
}
#[derive(Deserialize, Debug)]
pub struct Info<'a> {
pub field1: &'a str,
}
fn run<Callback>(mut callback: Callback)
where
Callback: FnMut(Info),
{
loop {
let content = message();
match serde_json::from_str::<Info>(&content) {
Ok(res) => callback(res),
Err(e) => eprintln!("Error: {}", e),
}
}
}
fn main() {
run(|info: Info| {
println!("info: {:?}", info);
});
}
This is fine, when run this example, it outputs: info: Info { field1: "value1" }
Then I try to make function run
more generic. My goal is to allow serde_json::from_str
deserialize message to any structure, not just Info
, so I introduce a generic parameter Res
, the run
function becomes:
fn run<'de, Res: Deserialize<'de>, Callback>(mut callback: Callback)
where
Callback: FnMut(Res),
{
loop {
let content = message();
match serde_json::from_str::<Res>(&content) {
Ok(res) => callback(res),
Err(e) => eprintln!("Error: {}", e),
}
}
}
But when I run the example, rust complains:
error[E0597]: `content` does not live long enough
--> examples/demo2.rs:18:43
|
12 | fn run<'de, Res: Deserialize<'de>, Callback>(mut callback: Callback)
| --- lifetime `'de` defined here
...
18 | match serde_json::from_str::<Res>(&content) {
| ----------------------------^^^^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `content` is borrowed for `'de`
...
22 | }
| - `content` dropped here while still borrowed
Apparently, there is no lifetime issue at runtime. But since Res was bound to lifetime 'de declared in run
function, so rust complains. (My understanding of lifetime here is I said Res
must live long enough than the scope of fuction run
, but actually it is not).
Is there any approach to work around this issue? Or other patterns to achive the same goal? Thanks for your help!
Here is the real project: yufuquant/rust-bybit: Rust API connector for Bybit's WebSockets APIs. (github.com)