How to get struct from one vector and pass it to another within a wrapping struct

In my code, I try to handle a situation when I have a Foo struct which contains two vectors of Bar structs. The Foo is pretty much an aggregate for some logic being done on the Bar structs. Please consider the following code:

pub struct Bar {
   parent_bar: Option<Box<Bar>>,
   val: i32,
}

pub struct Foo {
   board: Vec<Bar>,
   bars_to_further_process: Vec<Bar>,
}

impl Foo {
   pub fn new(&self) -> Self {
      Foo {
         board: Vec::with_capacity(10),
         bars_to_further_process: Vec::new(),
      }
   }

   // ...
   // some init method to populate board with Bar structs
   // ...

   pub fn process(&mut self) {
      let mut currentBar = self.board[0];
      let nextBar = self.board[1];

      // do some logic and change some values of currentBar
      currentBar.val = 10;

      // assign next bar as parent of the current
      currentBar.parent_bar = Some(Box::new(nextBar));

      self.bars_to_further_process.push(currentBar);
   }
}

The above, however, does not compile:

error[E0507]: cannot move out of borrowed content
--> src/astar2.rs:24:28
|
24 | let mut currentBar = self.board[0];
| ^^^^^^^^^^^^^
| |
| cannot move out of borrowed content
| help: consider borrowing here: &self.board[0]

error[E0507]: cannot move out of borrowed content
--> src/astar2.rs:25:21
|
25 | let nextBar = self.board[1];
| ^^^^^^^^^^^^^
| |
| cannot move out of borrowed content
| help: consider borrowing here: &self.board[1]

If I in fact do borrow, I get another error:

error[E0308]: mismatched types
--> src/astar2.rs:31:45
|
31 | currentBar.parent_bar = Some(Box::new(nextBar));
| ^^^^^^^ expected struct astar2::Bar, found reference
|
= note: expected type astar2::Bar
found type &astar2::Bar

error[E0308]: mismatched types
--> src/astar2.rs:33:41
|
33 | self.bars_to_further_process.push(currentBar);
| ^^^^^^^^^^ expected struct astar2::Bar, found reference
|
= note: expected type astar2::Bar
found type &astar2::Bar

This seems to be obvious. I have a Vec of Bar structs, not a Vec of Bar references. Should in this case bars_to_further_processing be declared as a Vec<&Bar> instead? If I do so, I am forced to specify lifetime parameters on the Foo struct itself which I need to avoid, as in wasm-bindgen it's not supported.
I am missing something here. Will be gratefull for any hints.

Some ideas which may be closer to what you're trying to do

You could move the bars out of the board:

let mut currentBar = self.board.remove(0);
let nextBar = self.board.remove(0);

You could clone the Bars, making a deep copy of them. (since you seem to be building some sort of linked list I don't imagine this is what you intend to do)

#[derive(Clone)]
pub struct Bar {
   parent_bar: Option<Box<Bar>>,
   val: i32,
}

let mut currentBar = self.board[0].clone();
let nextBar = self.board[1].clone();

It all boils down to what you intend the ownership model to be. The type Box<T> says "I have unique access to a T; I alone decide when it is destroyed." Perhaps you want something else, like:

  • Rc<T> which gives shared access to a T, destroyed only once no longer referenced
  • Rc<RefCell<T>> which further allows the T to be mutated (by deferring the borrow checker's work to runtime checks)
  • Arc<RwLock<T>> which further allows to be shared across threads

Of course, & and &mut also say certain things about ownership:

  • &T is a shared pointer; many things can access the T at once. (like Rc, except not responsible for destruction)
  • &mut T is a unique pointer. For as long as it exists, it is impossible to access the T any other way. (like Box, except not responsible for destruction)

But the guarantees of the latter two are statically checked by the borrow checker---hence the need for lifetimes.

1 Like

@ExpHP thank you for your post. The summary of the pointer types you mentioned actually made me go deeper into understanding how references and their ownership works (which I should have done in the first place, sorry).

The solution which works for me was to change the implementation of the Foo struct, which uses Rc along with RefCell:

pub struct Foo {
   board: Vec<Rc<RefCell<Bar>>>,
   bars_to_further_process: Vec<Rc<RefCell<Bar>>>,
}

This way, I can access the bars immutably or mutably by using self.board[0].borrow() or self.board[0].borrow_mut(), and also put them on the bars_to_further_process list by using Rc::clone().

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.