3

I have a global struct that stores my objects and has complicated behavior. Besides the data it has a global id-generator for objects. They need them for their work too.

use std::sync::{Arc, Mutex};

static mut system_ptr: *mut Box<System> = 0 as *mut Box<System>;

struct System {
    data: Vec<Data>,
    id_mutex: Arc<Mutex<u32>>,
}

impl System {
    fn get_new_id() -> u32 {
        unsafe {
            let s = &mut *system_ptr;
            let mut data = s.id_mutex.lock().unwrap();
            *data += 1;
            *data
        }
    }
}

I am initializing that struct like this now:

fn main() {
    let s = System{data: Vec::new(), id_mutex: Arc::new(Mutex::new(0 as u32))};
    let mut s_box = Box::new(s);
    unsafe {
        system_ptr = &mut s_box as *mut Box<System>;
    }

    // here I have some work with the initialized "System"

}

When I move initialization code from main() to some function, the Box is dropped and I have a "use after free" error and a crash.

I've tried to use &'static but am not fluent enough with Rust's semantics for now, or it was bad idea.

Anyway, how can I move initialization of some Box'ed memory (raw pointer) to a function or method?

Edit: This is not a question about singleton, it's about initialization of any heap variable. Matt, thanks for the right understanding!

Revertron
  • 1,213
  • 2
  • 13
  • 17
  • 2
    Is this really just a duplicate of [how to make a global mutable singleton](http://stackoverflow.com/questions/27791532/how-do-i-create-a-global-mutable-singleton)? – Shepmaster Oct 11 '15 at 21:06

1 Answers1

4

You may may to use the lazy_static crate to initialize your static variable, and then you won't need to deal with raw pointers.

But if you do want to handle this yourself, you can do it like this:

static mut system_ptr: *mut System = 0 as *mut System;

fn init() {
    let mut system = Box::new(System(Vec::new()));
    unsafe {
        system_ptr = &mut *system;
    }
    std::mem::forget(system);
}

Passing the box to std::mem::forget leaks it intentionally, ensuring that its destructor never runs. Instead of storing a pointer to the Box (which is itself just a pointer in a local stack variable), we store a pointer directly to the value on the heap.

The upcoming Rust 1.4 release will have a Box::into_raw function which handles all of this for you. If you upgrade to Rust 1.4 (currently on the beta channel), you can replace the above code with:

fn init() {
    unsafe {
        system_ptr = Box::into_raw(Box::new(System(Vec::new())));
    }
}
mbrubeck
  • 869
  • 6
  • 5
  • Why do you feel that this question is not a duplicate of the [one suggested](http://stackoverflow.com/questions/27791532/how-do-i-create-a-global-mutable-singleton)? – Shepmaster Oct 12 '15 at 15:32
  • 1
    There are sort of two questions here. One is "how do I initialize static variables" which is certainly a duplicate. But another is "how do I correctly initialize a raw pointer from a Box" which is the part I focused on. – mbrubeck Oct 12 '15 at 15:44