The boiled-down problem looks as follows:
use std::marker::PhantomData;
struct WorldState<'a> {
state: &'a f64,
}
trait CalculateWorldState<T> {
fn state_value(&mut self, input: &T) -> f64;
}
trait LearningAlgorithm<T> {
fn print_learning_information(&self, &T);
}
struct EvolutionaryAlgorithm<F, T>
where
F: CalculateWorldState<T>,
{
//I need this since I only use T as a method parameter, I do not save it anywhere
//T are different ways to represent the current worldstate and are
//short-lived (new ones generated every frame)
_p_: PhantomData<T>,
//I don't actually need this one in the real example since I have
//an instatiated version of type CalculateWorldState saved in the
//struct but I use phantomdata for simplicity of the example
_p: PhantomData<F>,
}
impl<F, T> LearningAlgorithm<T> for EvolutionaryAlgorithm<F, T>
where
F: CalculateWorldState<T>,
{
fn print_learning_information(&self, input: &T) {
println!("My learning goes splendid!");
//do something with &T by calling the object of type
//CalculateWorldState which we have saved somewhere, but do
//not save the &T reference anywhere, just look at it
}
}
struct WorldIsInGoodState {}
impl<'a> CalculateWorldState<WorldState<'a>> for WorldIsInGoodState {
fn state_value(&mut self, input: &WorldState) -> f64 {
100.
}
}
fn main() {
let mut a: Box<LearningAlgorithm<WorldState>> =
Box::new(EvolutionaryAlgorithm::<WorldIsInGoodState, WorldState> {
_p: PhantomData,
_p_: PhantomData,
});
{
let state = WorldState { state: &5. };
a.print_learning_information(&state);
}
}
The above code fails to compile:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:59:5
|
57 | let state = WorldState { state: &5. };
| -- temporary value created here
58 | a.print_learning_information(&state);
59 | }
| ^ temporary value dropped here while still borrowed
60 | }
| - temporary value needs to live until here
WorldState<'a>
is a very short-lived data type (one per frame), whereas LearningAlgorithm
is a very long-lived data type (multiple games). But the way I implemented the thing, Rust is eager to believe, that every WorldState
I pass to print_learning_information
has to outlive the LearningAlgorithm
.
What did I do wrong? How could this else be handled?
A few things I would not like to do:
- Have
WorldState
contain a normal state (since in reality it contains a few vectors and not af64
and I don't want to copy them around intoWorldState
structs when passing each player its own view of the world) - Just quit this project and start a new one (you all know it, after you invested some time, you don't want to just throw all the work away)
Your problem can be boiled down to
Note that
WorldState
is a type constructor, not a concrete type. Lifetime elision allows you to writeBox<LearningAlgorithm<WorldState>>
with no lifetime parameter explicitly specified forWorldState
, but it just means that the compiler selects some appropriate lifetime parameter.In this case lifetime selected for
WorldState
isscope a
, thus the type ofa
isBox<LearningAlgorithm<WorldState<'scope_a>>>
. Consequently,state
should have typeWorldState<'scope_a>
, and the reference it contains should be valid forscope a
, but a value the reference points to exists only inscope b
.You need support for higher-kinded types to make your example work as it is, but Rust doesn't provide it.
The easiest solution is to get rid of
WorldState
's lifetime parameter by replacing reference withRc
. Maybe someone will come up with better solution.