user19463299
user19463299

Reputation: 33

Rust SDL2 Timer: How can I return timer object from the function?

I am using sdl2 crate and I am trying to create a simple "context" object with timer inside of it. I have the following code

extern crate sdl2;

struct Ctx<'a, 'b> {
    timer: sdl2::timer::Timer<'a, 'b>,
}

impl<'a,'b> Ctx<'a, 'b> {
    pub fn new() -> Ctx<'a,'b> {
        let sdl_context = sdl2::init().unwrap();
        let timer_subsystem = sdl_context.timer().unwrap();
        let timer = timer_subsystem.add_timer(100, Box::new(|| -> u32 { 100 }));

        Ctx { timer }
    }
}

pub fn main() {
    let _ = Ctx::new();
}

The problem is that add_timer() seem to borrow timer_subsystem object, and it prevents timer to be moved out of the function.

error[E0515]: cannot return value referencing local variable `timer_subsystem`
  --> src/main.rs:13:9
   |
11 |         let timer = timer_subsystem.add_timer(100, Box::new(|| -> u32 { 100 }));
   |                     ----------------------------------------------------------- `timer_subsystem` is borrowed here
12 |
13 |         Ctx { timer }
   |         ^^^^^^^^^^^^^ returns a value referencing data owned by the current function

However, if I look into add_timer(), I cannot see why does the reference is considered still alive. In fact the self reference is not used in the function at all: https://docs.rs/sdl2/latest/src/sdl2/timer.rs.html#17-32

    pub fn add_timer<'b, 'c>(&'b self, delay: u32, callback: TimerCallback<'c>) -> Timer<'b, 'c> {
        unsafe {
            let callback = Box::new(callback);
            let timer_id = sys::SDL_AddTimer(
                delay,
                Some(c_timer_callback),
                mem::transmute_copy(&callback),
            );

            Timer {
                callback: Some(callback),
                raw: timer_id,
                _marker: PhantomData,
            }
        }
    }

I was trying to put both timer and timer_subsystem to Ctx (to make sure their lifetime is the same), but it didn't help. Neither wrapping them with Box<> (to prevent having a dangling pointer after timer_subsystem was moved), but neither worked.

Why of I have a reference to local data here and how can I properly return the timer object?

Upvotes: 3

Views: 321

Answers (1)

Chayim Friedman
Chayim Friedman

Reputation: 70940

The secret is in the PhantomData. The full type as declared inside Timer is PhantomData<&'b ()>, and 'b is specified as &'b self in add_timer(). This ties the lifetime of the returned Timer to the lifetime of the TimerSubsystem. This is because when the subsystem is dropped it is shutted down, and it is not permitted to access the timer afterward.

The best option is probably to initialize the timers subsystem (and SDL) before calling Ctx::new() and passing it as an argument, as it is hard to store both the subsystem and the timer (a self-referential struct).

Upvotes: 2

Related Questions