I would like to implement a trait function that takes a path and gives an owned value.
use bevy::prelude::*;
use serde::de;
pub trait RonResource<'a, T: de::Deserialize<'a> + Resource> {
fn import_from_ron(path: &str) -> T {
let ron_string = std::fs::read_to_string(path).unwrap();
ron::from_str::<T>(
&ron_string
).expect(("Failed to load: ".to_owned() + path).as_str())
}
}
I implement the 'a lifetime because de::Deserialize needs it. But the compiled tells me that "ron_string" will be dropped while still borrowed. (at the "&ron_string" line)
This code works fine if I implement it without generics, like this for exemple:
let settings = ron::from_str::<GasParticleSystemSettings>(
&std::fs::read_to_string("assets/settings/gas_ps_settings.ron").unwrap()
).expect("Failed to load settings/gas_ps_settings.ron")
.side_gas;
I don't get why the value need to "survive" the whole function as it will not be needed. Otherwise, the specific code wouldn't work!
The problem is when using Deserialize<'a>
the deserialized object might contain references to the original str
ie something like this:
struct HasRef<'a> {
s: &'a str,
}
is allowed. It would contain references to ron_string
which is dropped at the end of the function.
What's totally save though is to just require DeserializeOwned
instead at which point you can't do 0-copy deserialization any more but you don't have to keep the original string around either:
pub trait RonResource<T: de::DeserializeOwned + Resource> {
fn import_from_ron(path: &str) -> T {
let ron_string = std::fs::read_to_string(path).unwrap();
ron::from_str::<T>(
&ron_string
).expect(("Failed to load: ".to_owned() + path).as_str())
}
}
Your second example works presumably because you only access GasParticleSystemSettings.side_gas
which I assume either does not contain references or is straight up Copy
neither of which would create a lifetime issue.