Search code examples
rustlifetimeborrow-checkerserde-json

serde_json converting a String to a type with lifetime annotation causing issues


pub async fn checkin_seed_node<'a>(appstate: Arc<AppState<'a>>) {
    loop {
        let response: Response = call_seed_node(&appstate.client).await;
        let body = response
            .text()
            .await
            .expect("failed to get text from response");
        // &body causes borrow issues.
        let nodes: Vec<Node<'_>> =
            serde_json::from_str(&body.as_str()).expect("failed to parse response");

        for node in &nodes {
            let mut lock = appstate.internal.lock();
            let mut map = lock.borrow_mut();
            map.insert(
                100,
                Node {
                    address: node.address,
                },
            );
        }
    }
}

// using reqwest client call an endpoint
async fn call_seed_node(client: &Client) -> Response {
    time::sleep(time::Duration::from_secs(2)).await;
    let response = client
        .get("http://localhost:8080/node-config")
        .send()
        .await
        .expect("failed to get a response");
    response
}

// Node struct
pub struct Node<'a> {
    pub address: &'a str,
}

impl<'a> Node<'a> {
    pub fn new(address: &'a str) -> Self {
        Node { address }
    }
}

When I try to compile this code I run into the following compile error.

error[E0597]: `body` does not live long enough
  --> src/lib.rs:20:35
   |
   |   pub async fn checkin_seed_node<'a>(appstate: Arc<AppState<'a>>) {
   |                                      -------- lifetime `'1` appears in the type of `appstate`
...
   |               serde_json::from_str(&body.as_str()).expect("failed to parse response");
   |                                     ^^^^^^^^^^^^^ borrowed value does not live long enough
...
   | /             map.insert(
   | |                 100,
   | |                 Node {
   | |                     address: node.address,
   | |                 },
   | |             );
   | |_____________- argument requires that `body` is borrowed for `'1`
   |           }
   |       }
   |       - `body` dropped here while still borrowed

For more information about this error, try `rustc --explain E0597`.

I am new to Rust so this is my first time dealing with lifetime annotations. I tried using Box, hoping that pushing the data onto the Heap might help but I run into the same issue.


Solution

  • Deserializing into a borrowed string slice is most definitely not what you want, for example deserializing JSON like this will fail:

    fn main() {
        let s = r#""hello\"world""#;
        let st: &str = serde_json::from_str(s).unwrap();
    }
    

    because deserializing strings that include escape sequences cannot be done without modifing the string.

    Further you need something that owns the address and the best place to store it is honestly just the Node so the fix is to not borrow data in Node at all:

    #[derive(serde::Deserialize)]
    struct Node {
        address: String,
    }