Caballero
Caballero

Reputation: 12111

rustc_serialize::Json value borrowed in a while loop does not live enough

This code:

extern crate rustc_serialize;

use rustc_serialize::json;
use rustc_serialize::json::Json;

fn main() {
    let mut string: String = "{\"num\": 0}".to_string();

    let mut obj = Json::from_str(&string).unwrap().as_object().unwrap();

    let mut num = obj.get("num").unwrap().as_u64().unwrap();

    for i in 1..10 {
        println!("{:?}", num);
        string = format!("{}{}{}", "{\"num\": ", i, "}");
        obj = Json::from_str(&string).unwrap().as_object().unwrap();
        num = obj.get("num").unwrap().as_u64().unwrap();
    }
}

Produces an error

error: borrowed value does not live long enough
 --> src/main.rs:9:19
  |>
9 |>     let mut obj = Json::from_str(&string).unwrap().as_object().unwrap();
  |>                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ does not live long enough
note: reference must be valid for the block suffix following statement 1 at 9:72...
 --> src/main.rs:9:73
  |>
9 |>     let mut obj = Json::from_str(&string).unwrap().as_object().unwrap();
  |>                                                                         ^
note: ...but borrowed value is only valid for the statement at 9:4
 --> src/main.rs:9:5
  |>
9 |>     let mut obj = Json::from_str(&string).unwrap().as_object().unwrap();
  |>     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider using a `let` binding to increase its lifetime
 --> src/main.rs:9:5
  |>
9 |>     let mut obj = Json::from_str(&string).unwrap().as_object().unwrap();
  |>     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: borrowed value does not live long enough
  --> src/main.rs:16:15
   |>
16 |>         obj = Json::from_str(&string).unwrap().as_object().unwrap();
   |>               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ does not live long enough
note: reference must be valid for the block suffix following statement 1 at 9:72...
 --> src/main.rs:9:73
  |>
9 |>     let mut obj = Json::from_str(&string).unwrap().as_object().unwrap();
  |>                                                                         ^
note: ...but borrowed value is only valid for the statement at 16:8
  --> src/main.rs:16:9
   |>
16 |>         obj = Json::from_str(&string).unwrap().as_object().unwrap();
   |>         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider using a `let` binding to increase its lifetime
  --> src/main.rs:16:9
   |>
16 |>         obj = Json::from_str(&string).unwrap().as_object().unwrap();
   |>         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

I don't understand what's the issue here, is the error misleading or something? Why is it pointing at Json - that doesn't make much sense. Also due to the nature of my code I can't create another variable inside the loop - the old one must be reused.

Upvotes: 1

Views: 150

Answers (2)

mcarton
mcarton

Reputation: 30061

as_object returns a reference to the inner map of a temporary JSON object. Since the JSON object is temporary, the inner map is too. You can unwrap the map easily though:

extern crate rustc_serialize;

use rustc_serialize::json;
use rustc_serialize::json::{Json, Object};

fn main() {
    // while the online documentation of Json has a `into_object` method,
    // the local nightly rustc I have does not have it anymore
    fn into_object(json: Json) -> Option<Object> {
        if let Json::Object(map) = json {
            Some(map)
        } else {
            None
        }
    }

    let mut string: String = "{\"num\": 0}".to_string();

    let mut obj = into_object(Json::from_str(&string).unwrap()).unwrap();

    let mut num = obj.get("num").unwrap().as_u64().unwrap();

    for i in 1..10 {
        println!("{:?}", num);
        string = format!("{}{}{}", "{\"num\": ", i, "}");
        obj = into_object(Json::from_str(&string).unwrap()).unwrap();
        num = obj.get("num").unwrap().as_u64().unwrap();
    }

}

Upvotes: 2

Pavel Strakhov
Pavel Strakhov

Reputation: 40512

The issue is that you use as_object incorrectly. The name of the method ("as", not "to" or "into") indicates that it just represents the Json object as another type. Return type of as_object is Option<&'a mut Object>, which tells us that it only returns a reference that lives as long as the Json object itself. When you use it in a one-liner, the borrow checker is not happy because intermediate Json object would be deleted after this line, but you try to use the reference that remains.

A solution is to store Json in a variable and don't store a reference at all:

fn main() {

    let mut string: String = "{\"num\": 0}".to_string();

    let mut json = Json::from_str(&string).unwrap();
    let mut num = json.as_object().unwrap().get("num").unwrap().as_u64().unwrap();

    for i in 1..10 {
        println!("{:?}", num);
        string = format!("{}{}{}", "{\"num\": ", i, "}");
        json = Json::from_str(&string).unwrap();
        num = json.as_object().unwrap().get("num").unwrap().as_u64().unwrap();
    }

}

If you store result of as_object in a variable, Rust won't let you re-assign json variable because its content is borrowed. If you really want to do it, you need to put the variable in a scope so that it is no more available at the beginning of the loop.

Upvotes: 4

Related Questions