Achim
Achim

Reputation: 11

allocating data structures while making the borrow checker happy

I'm writing my first rust program and as expected I'm having problems making the borrow checker happy. Here is what I'm trying to do:

I would like to have a function that allocates some array, stores the array in some global data structure, and returns a reference to it. Example:

static mut global_data = ...

fn f() -> &str {
  let s = String::new();
  global.my_string = s;
  return &s;
};

Is there any way to make something like this work? If not, what is "the rust way"(tm) to get an array and a pointer into it?

Alternatively, is there any documentation I could read? The rust book is unfortunately very superficial on most topics.

Upvotes: 1

Views: 68

Answers (2)

Achim
Achim

Reputation: 11

Okay, just in case someone else is having the same question, the following code seems to work:

struct foo {
  b : Option<Box<u32>>,
}

static mut global : foo = foo { b : None };

fn f<'a>() -> &'a u32 {
  let b : Box<u32> = Box::new(5);

  unsafe {
    global.b = Some(b);

    match &global.b {
      None    => panic!(""),
      Some(a) => return &a,
    }
  }
}

At least it compiles. Hopefully it will also do the right thing when run.

I'm aware that this is not how you are supposed to do things in rust. But I'm currently trying to figure out how to implement various data structures from scratch, and the above is just a reduced example of one of the problems I encountered.

Upvotes: 0

Aleksander Krauze
Aleksander Krauze

Reputation: 6080

There are a couple things wrong with your code:

  1. Using global state is very unidiomatic in rust. It can be done in some specific scenarios, but it should never be a go to method. You cold try wrapping your state in Rc or Arc and share it this way in your program. If you also want to mutate this state (as you show in your example) you must to wrap it also in some kind of interior mutability type. So try Rc<RefCell<State>> if you want to use state in only one thread or Arc<Mutex<State>> if you want to use it from multiple different threads.

  2. Accessing mutable static memory is unsafe. So even the following code won't compile:

static mut x: i32 = 0;

// neither of this lines work!
println!("{}", x);
x = 42;

You must use unsafe to access or modify any static mutable variables, because you must de facto prove to the compiler that you assure it that no data races (from accessing this data from different threads) will occur.

  1. I can't be sure, since you didn't show what type is global_data, but I assume, that my_string is a field of type String. When you write
let s = String::new();
global.my_string = s;

You move ownership of that string to the global. You therefore cannot return (or even create) reference to it. You must do this though it's new owner. &global.my_string could work, but not if you do what I written in 1. You could try to return RefMut of MutexGuard, but that is probably not what you want.

Upvotes: 3

Related Questions