swift-lynx
swift-lynx

Reputation: 3765

How can I change the value of a field of a boxed struct while making sure the pointer to the box stays valid?

I have the following rust code:

pub struct Settings {
  pub vec: Vec<usize>,
  pub index: usize,
}

pub fn add_to_settings_vec(ptr: *mut Settings) {
  // create a box from the pointer
  let boxed_settings = unsafe { Box::from_raw(ptr) };

  // get the value in the box
  let mut settings = *boxed_settings;

  // mutate the value
  settings.vec.push(123);

  // create a new box and get a raw pointer to it so the value isn't deallocated
  Box::into_raw(Box::new(project));
}

I want to make sure that the pointer that was passed as a function parameter stays valid and points to the modified Settings value, even after adding an element to the vector of the unboxed Settings value.

I think I'll need the following steps to achieve my goal:

This should probably be possible as the Settings value is of a fixed size, as far as I have understood it, even with the vector, as the vector grows its the memory at a different location.

I have looked at the docs over at https://doc.rust-lang.org/std/boxed/struct.Box.html but I haven't been able to figure this out.

I have also read about pins, but I do not know how or when to use them: https://doc.rust-lang.org/std/pin/index.html.

Does the above code always leave the passed in pointer in tact? I'd appreciate an explanation on why or why not.

If not, how can I make sure that the passed in pointer stays valid and keeps pointing to the modified Settings value, at the same place in memory?

Upvotes: 0

Views: 1218

Answers (2)

Masklinn
Masklinn

Reputation: 42592

Unbox the value
Modify the settings in place
Make sure that the Settings don't get deallocated

It's already been deallocated at step (1): when you deref'd it, you moved the Settings struct out of the Box, and in doing so deallocated the box.

I don't understand why you go through all this mess (or why you pass unsafe pointers to a safe non-extern function).

// create a box from the pointer
let mut settings = unsafe { Box::from_raw(ptr) };

// mutate the value
settings.vec.push(123);

settings.leak();

would work fine and not dealloc anything. Though it's still risky and unnecessary to get a handle on a Box if the pointer is only borrowed (which it seems to be here since the settings are never returned.

A much better way would be to just create a borrow of the settings vec:

let v = unsafe { &mut (*ptr).vec };
v.push(123);

Or possibly the settings struct if you also need to update the index:

let settings = unsafe { &mut (*ptr) };
settings.vec.push(123);

Upvotes: 2

Netwave
Netwave

Reputation: 42786

You can achive the mutation by just dereferencing the pointer in an unsafe block:

pub struct Settings {
  pub vec: Vec<usize>,
  pub index: usize,
}

pub fn add_to_settings_vec(ptr: *mut Settings) {
  // mutate the value
  unsafe {(*ptr).vec.push(123)};
}

fn main() {
    let mut s = Settings {
        vec: vec![],
        index: 10,
    };
    
    add_to_settings_vec(&mut s as *mut Settings);
    println!("{:?}", s.vec);
}

Playground

Upvotes: 1

Related Questions