ron-rs/ron

Question on `from_str` complains about lifetime when coping with generics

BobAnkh opened this issue · 2 comments

I would like to read from file/string in a struct with generic, however I face the lifetime error.

The minimal reproduce code:

use ron;
use serde::{Deserialize, Serialize};

pub struct BarBuilder {
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Bar<T: Default> {
    bar: T,
}

impl BarBuilder {
    pub fn build<'de, T: Default + serde::de::Deserialize<'de>>(
        self,
    ) -> Bar<T>{
        let content = "Bar { bar: 1 }".to_string();
        let bar = ron::de::from_str(&content).unwrap();
        Bar { bar }
    }
}

fn main() {
    println!("Hello, world!");
}

The error msg:

$ cargo check
error[E0597]: `content` does not live long enough
  --> src\main.rs:16:37
   |
12 |     pub fn build<'de, T: Default + serde::de::Deserialize<'de>>(
   |                  --- lifetime `'de` defined here
...
16 |         let bar = ron::de::from_str(&content).unwrap();
   |                   ------------------^^^^^^^^-
   |                   |                 |
   |                   |                 borrowed value does not live long enough
   |                   argument requires that `content` is borrowed for `'de`    
17 |         Bar { bar }
18 |     }
   |     - `content` dropped here while still borrowed

For more information about this error, try `rustc --explain E0597`.
error: could not compile `demo` due to previous error

My dependencies in Cargo.toml:

[dependencies]
ron = "0.8"
serde = { version = "1.0", features = ["derive"] }

The same error occurs the same with from_reader.

I wonder why this piece of code is not working in ron while there is some way to bypass in toml:

pub struct BarBuilder {
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Bar<T: Default> {
    bar: T,
}

impl BarBuilder {
    pub fn build<'de, T: Default + serde::de::Deserialize<'de> + Clone>(
        self,
    ) -> Bar<T>{
        let content = "bar = 1".to_string();
        let configs: toml::value::Table = match toml::from_str(&content) {
            Ok(t) => t,
            Err(e) => {
                panic!("Error: {}", e);
            }
        };
        let configs: HashMap<String, T> = configs
            .iter()
            .map(|(k, v)| (k.to_owned(), v.to_owned().try_into().unwrap()))
            .collect();
        // let bar = toml::from_str(&content).unwrap();
        Bar { bar: configs["bar"].clone() }
    }
}

So I would like to know if there is one way for me to implement reading from ron within a genereic struct? Thank you!

I think the problem here is that while you define T: Deserialize<'de>, the &str you deserialise from has a different, shorter lifetime. You could either change the bound to T: DeserializeOwned, or if T might borrow data from the content string, pass content: &'de str to the build method itself.

The toml version seems to work because you first deserialise into their Value type and then clone that, erasing the lifetime requirement.

I hope this helps :)

@MomoLangenstein Really thank you for your suggestion! Switching to T: DeserializedOwned works.