2

I need to create a Vec to track the objects I'm creating and change it's states to use later. But if I use a clone when get object stored on vec, its state is not updated how do I do this?

#[derive(Debug, Clone)]
struct Movement {
  x: i32,
  y: i32,
}

fn main() {
   let mut current = Some(Movement { x: 1, y: 2 });

   let mut stack = Vec::new();

   stack.push(&current);

   current.as_mut().unwrap().x = 2;

   println!("m: {:?}", current);
   println!("stack.m: {:?}", stack.pop());
   current = None;
}
Jeriel
  • 117
  • 3
  • 8
  • It depends, but most likely you need some combination of RC and RefCell, but you may want to consider adjusting your design. Rust does not make shared mutable state easy. – user1937198 Aug 13 '20 at 23:03
  • Is there a reason you can't store the structs in the get directly? – user1937198 Aug 13 '20 at 23:04
  • I'm capturing the mouse events and storing the position inside this struct, the code run in loop and switch the current variable, in some point I iterate the vec accessing the values, the code is more complex than this, but this express the Idea. – Jeriel Aug 13 '20 at 23:36
  • 1
    Maybe just store indices into your vector? – phimuemue Aug 14 '20 at 07:18
  • The storing all of the mouse events in the vector and tracking the index of the current event is likely to be easier. – user1937198 Aug 14 '20 at 08:46
  • You are violating a very important rule of rusts owhnership guarantees here: You can either have multiple const references or one mutable reference. You are storing a reference to `current` into you `stack` and in the very next line you are requesting a mutable reference. That doesn't work out very well. You need to synchronize the access, have a look at the [`std::sync`](https://doc.rust-lang.org/std/sync/index.html#structs) module and choose one. – hellow Aug 14 '20 at 09:07
  • I feel like there ought to be a duplicate for this Q, but I could not find a good one. The ones I considered are linked in my answer. – trent Aug 14 '20 at 14:41

2 Answers2

6

You're trying to mutate something while holding a shared reference to it somewhere else. This is a problem because Rust assumes things behind shared (&) references will not be mutated, in order to prove your code is correct in multi-threaded contexts and in the presence of certain optimizations.

To fix it, you could tell the compiler to forget about multi-threading and skip those optimizations. One way to do that is using RefCell:

use std::cell::RefCell;

#[derive(Debug, Clone)]
struct Movement {
    x: i32,
    y: i32,
}

fn main() {
    let mut current = Some(RefCell::new(Movement { x: 1, y: 2 }));
    //                     ^^^^^^^^^^^^ put it in a `RefCell`
    let mut stack = Vec::new();

    stack.push(&current);

    // note: as_ref, not as_mut
    current.as_ref().unwrap().borrow_mut().x = 2;

    println!("m: {:?}", current);
    println!("stack.m: {:?}", stack.pop());
    current = None;
}

If you need to share the object around different threads, you may want Mutex or RwLock instead. If the thing being shared is something trivially copiable, like a single i32, you could use Cell<i32> (non-thread-safe) or AtomicI32 (thread-safe). Collectively these are called interior mutability types because they allow mutation of the internal value through a shared reference.

But interior mutability only gets you so far. Putting references to local variables in a Vec is not very flexible because the Vec cannot outlive any of the variables that it references. A common idiom is to move objects into a Vec and use indices, rather than built-in references, to access them. Here's one way to do that:

#[derive(Debug, Clone)]
struct Movement {
    x: i32,
    y: i32,
}

fn main() {
    // this object will be moved into the vector
    let temp = Some(Movement { x: 1, y: 2 });

    let mut stack = Vec::new();

    // now we set `current` to be the index of the object in the vector, and use
    // `stack[current_index]` to refer to the object itself
    let current_index = stack.len();
    stack.push(temp);
    
    // you could also do something like this to get a mutable reference, but it
    // would prevent you from doing anything else with `stack` until you are done with
    // `current` (`stack[current_index]` does not have this limitation):
    //let current = stack.last_mut().unwrap()

    stack[current_index].as_mut().unwrap().x = 2;

    println!("m: {:?}", stack[current_index]);
    println!("stack.m: {:?}", stack.pop());
}

There are more options. For instance, you could put the RefCell in an Rc (reference-counted smart pointer) and clone it to share the ownership between the Vec and a local variable. In the absence of more information about your goals I would lean toward indexing the Vec in order to avoid the complication of shared mutation, but the design is ultimately your choice.

See also

trent
  • 25,033
  • 7
  • 51
  • 90
  • _would prevent you from doing anything else with `stack`_ As far as i know it should behave roughly same with `index_mut` , can you explain if I am wrong? https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1a63c7bfbaf35e05403ab89600f4f111 – Ömer Erden Aug 15 '20 at 11:21
  • 1
    @ÖmerErden You are right, you can use `stack` again but not until after you're done with `current`. So something like [this](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0827cdaac6b0b3d85d5fa4c028615dfa) does not work. Storing only an index does not have this problem because an index does not borrow the `Vec` so every time you access `stack[current_index]` it uses a "fresh" borrow. (I edited the question to be more explicit.) – trent Aug 15 '20 at 16:23
  • Now it is more clear thanks for the explanation. I assumed OP has the ownership of the objects and using Stack(DS) to find out the next object's reference, not all the elements of stack, this might still need to be clarified though. If it is like that OP can always access the current element by using `last_mut()`, but yes if OP wants to access arbitrary element from the stack then `last_mut()` will not be useful. – Ömer Erden Aug 15 '20 at 16:54
  • It's worked with the index solution, was a little complicated because I had to refactor a little bit but I avoided some borrow checker problems. – Jeriel Aug 16 '20 at 04:01
0

To get a mutable reference to a value in a vec you can do the following:

let idx = stack.len() - 1;
if let Some(pos) = stack.get_mut(idx) {
  pos.x = 2
} else {
  ..
}

This specifically gets the last object you put in the vec with stack.push(current). Note that I would then move the objects into the vec completely and not just references to them, and access them using the above method. Here is your example corrected for this:

#[derive(Debug, Clone)]
struct Movement {
  x: i32,
  y: i32,
}

fn main() {
  let mut stack = Vec::new();
  stack.push(Movement { x: 1, y: 2 });

  let idx = stack.len()-1;
  let mut current = stack.get_mut(idx).unwrap();
  current.x = 2;

  println!("m: {:?}", current);
  println!("stack.m: {:?}", stack.pop());
}
trent
  • 25,033
  • 7
  • 51
  • 90
Emoun
  • 2,297
  • 1
  • 13
  • 20
  • 1
    The explanation is right but the code is very wrong. If you're going to post code in an answer please be sure it does what you say it will. – trent Aug 14 '20 at 13:20
  • 2
    Using `last()` or `last_mut()` might be a better choice to emphasize stack behaviour(peek). – Ömer Erden Aug 15 '20 at 11:10
  • @ÖmerErden yes you are right, but i chose to focus on `get_mut` since OP said he might be accessing other elements too, so wanted to use a method that could be used for that too – Emoun Aug 15 '20 at 11:24