QuantumBadger / Speedy2D

Rust library for hardware accelerated drawing of 2D shapes, images, and text, with an easy to use API.
Apache License 2.0
381 stars 41 forks source link

WindowHandler lifetime issue #70

Closed d0rianb closed 1 year ago

d0rianb commented 1 year ago

I can't figure out how to use references in struct belonging to the WindowHandler because of lifetime errors : error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement'. The WindowHandler lifetime is suppose to be static because it lives as long as the program lives but there is no way to specify it to the compiler. I try by changing the 'static lifetime on the Context into anonymous but it's still the same error. Is it any solution ? A simple replication of my issue is down below :

use speedy2d::window::{KeyScancode, VirtualKeyCode, WindowHandler, WindowHelper};
use speedy2d::Window;

fn main() {
    let window = Window::new_centered("Speedy2D: test", (800, 600)).unwrap();

    window.run_loop(MyWindowHandler {
        context: Context { blocks: vec![Block {}], selected_block: &Block {} },
    })
}

struct Block;

struct Context<'ctx> {
    blocks: Vec<Block>,
    selected_block: &'ctx Block,
}

impl<'ctx> Context<'ctx> {
    pub fn add_selected_block(&'ctx mut self) {
        self.selected_block = &self.blocks[0];
    }
}

struct MyWindowHandler {
    context: Context<'static>,
}

impl WindowHandler for MyWindowHandler {
    fn on_key_down(&mut self, _helper: &mut WindowHelper, _virtual_key_code: Option<VirtualKeyCode>, _scancode: KeyScancode) {
        self.context.add_selected_block();
    }
}
QuantumBadger commented 1 year ago

Hi @d0rianb, I think what you're trying to do is forbidden by Rust.

struct Context<'ctx> {
    blocks: Vec<Block>,
    selected_block: &'ctx Block,
}

In Rust, self-referential structs are forbidden -- in other words, it's not possible to have a struct hold a reference to a thing which is also in that struct. I'd recommend changing this to:

struct Context {
    blocks: Vec<Rc<Block>>,
    selected_block: Rc<Block>,
}

Or alternatively, selected_block could be an integer or other ID, which shows which block in the Vec is selected.

d0rianb commented 1 year ago

Ok I will try this but I don't think it's a self-referential struct because selected_block refer to a Block not to the context.

QuantumBadger commented 1 year ago

I think a self-referential struct just means any struct which has a reference to either itself, or something inside itself.

To give a specific example of the safety issue, let's say you set self.selected_block to &self.blocks[0], and then delete all the elements in self.blocks. This would mean self.selected_block now points to something which doesn't exist (i.e. a dangling reference).

d0rianb commented 1 year ago

Ok I understand I will implement the Rc solution, thanks