Hi,
I've been trying to learn Rust by writing some ECS-like code, and having a lot of difficulty with lifetimes.
I've trimmed down my code so that it still produces the same type of errors I'm seeing, and put it in a playground.
What I'm trying to do is store a list of callbacks that will be invoked later on, and passed in some arguments that are retrieved from a context. It's the references to this context that I can't seem to get right.
The build errors:
error[E0491]: in type `&'a Ref<'entity, <T as SystemArgument<'a, 'entity>>::Component>`, reference has a longer lifetime than the data it references
--> src/lib.rs:70:82
|
70 | let component_as_arg = <T as SystemArgument<'a, 'entity>>::into_arg_type(&component_ref);
| ^^^^^^^^^^^^^^
|
note: the pointer is valid for the lifetime `'a` as defined on the impl at 62:6
--> src/lib.rs:62:6
|
62 | impl<'a, 'entity, T> System<'entity> for Callback<T>
| ^^
note: but the referenced data is only valid for the lifetime `'entity` as defined on the impl at 62:10
--> src/lib.rs:62:10
|
62 | impl<'a, 'entity, T> System<'entity> for Callback<T>
| ^^^^^^^
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/lib.rs:70:82
|
70 | let component_as_arg = <T as SystemArgument<'a, 'entity>>::into_arg_type(&component_ref);
| ^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'entity` as defined on the impl at 62:10...
--> src/lib.rs:62:10
|
62 | impl<'a, 'entity, T> System<'entity> for Callback<T>
| ^^^^^^^
note: ...so that the type `Ref<'entity, <T as SystemArgument<'a, 'entity>>::Component>` is not borrowed for too long
--> src/lib.rs:70:82
|
70 | let component_as_arg = <T as SystemArgument<'a, 'entity>>::into_arg_type(&component_ref);
| ^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 62:6...
--> src/lib.rs:62:6
|
62 | impl<'a, 'entity, T> System<'entity> for Callback<T>
| ^^
note: ...so that reference does not outlive borrowed content
--> src/lib.rs:70:82
|
70 | let component_as_arg = <T as SystemArgument<'a, 'entity>>::into_arg_type(&component_ref);
| ^^^^^^^^^^^^^^
Code: Rust playground
use std::cell::Ref;
use std::cell::RefCell;
#[derive(Default)]
struct Health(f32);
struct Entity {
health: RefCell<Health>,
}
impl Default for Entity {
fn default() -> Self {
Self {
health: RefCell::new(Health::default()),
}
}
}
trait RetrieveComponent<'entity, T> {
fn retrieve(&'entity self) -> Ref<'entity, T>;
}
impl<'entity> RetrieveComponent<'entity, Health> for Entity {
fn retrieve(&'entity self) -> Ref<'entity, Health> {
self.health.borrow()
}
}
trait SystemArgument<'a, 'entity> {
type Component;
fn retrieve_component(storage: &'entity Entity) -> Ref<'entity, Self::Component>
where
Entity: RetrieveComponent<'entity, Self::Component>;
fn into_arg_type(borrowed_ref: &'a Ref<'entity, Self::Component>) -> Self;
}
impl<'a, 'entity, T> SystemArgument<'a, 'entity> for &'a T {
type Component = T;
fn retrieve_component(entity: &'entity Entity) -> Ref<'entity, Self::Component>
where
Entity: RetrieveComponent<'entity, Self::Component>,
{
entity.retrieve()
}
fn into_arg_type(borrowed_ref: &'a Ref<'entity, Self::Component>) -> Self {
&*borrowed_ref
}
}
trait System<'entity> {
fn run(&self, storage: &'entity Entity);
}
struct Callback<T> {
callback: Box<dyn Fn(T)>,
}
impl<'a, 'entity, T> System<'entity> for Callback<T>
where
T: SystemArgument<'a, 'entity>,
Entity: RetrieveComponent<'entity, T::Component>,
{
fn run(&self, entity: &'entity Entity) {
let component_ref: Ref<'entity, T::Component> =
<T as SystemArgument<'a, 'entity>>::retrieve_component(entity);
let component_as_arg = <T as SystemArgument<'a, 'entity>>::into_arg_type(&component_ref);
(self.callback)(component_as_arg);
}
}
fn print_health(health: &Health) {
println!("print_health, health: {}", health.0);
}
fn main() {
let entity = Entity::default();
let mut systems: Vec<Box<dyn System<'_>>> = Vec::new();
systems.push(Box::new(Callback {
callback: Box::new(print_health),
}));
for sys in &systems {
sys.run(&entity);
}
}
Note that the stripped down code is a bit contrived
Normally there's also an impl of SystemArgument for &'a mut T
, and a bunch of other stuff.
Any hints would be greatly appreciated, thanks in advance!
Marc