How to mutate &Vec?

I'm a beginner trying to solve this problem: Two Bucket in Rust on Exercism.

Not the complete code, but should be enough for this problem.

pub fn solve(
    capacity_1: u8,
    capacity_2: u8,
    goal: u8,
    start_bucket: &Bucket,
) -> Option<BucketStats> {
    if goal == 0 {
        return None;
    }
    let mut explored = HashSet::new();
    let mut frontier = VecDeque::new();
    let start = if start_bucket == &Bucket::One {
        vec![(capacity_1, 0, Move::FillOne)]
    } else {
        vec![(0, capacity_2, Move::FillTwo)]
    };
    frontier.push_back(&start);

    while !frontier.is_empty() {
        let mut path = frontier.pop_front().unwrap();
        let (x, y, _) = path[path.len() - 1];
        for state in successors(x, y, capacity_1, capacity_2)
            .iter()
            .filter(|&s| !is_smaller_full_and_larger_empty(s.0, s.1, capacity_1, capacity_2)) {
            if explored.contains(&(state.0, state.1)) {
                continue;
            }
            explored.insert((state.0, state.1));
            path.push(state.clone());
            if state.0 == goal || state.1 == goal {
                println!("{:?}", path);
                return None;
            }
            frontier.push_back(path);
        }
    }
    None
}

But it fails compilation.

warning: variable does not need to be mutable
  --> src/lib.rs:52:13
   |
52 |         let mut path = frontier.pop_front().unwrap();
   |             ----^^^^
   |             |
   |             help: remove this `mut`
   |
   = note: `#[warn(unused_mut)]` on by default

error[E0596]: cannot borrow `*path` as mutable, as it is behind a `&` reference
  --> src/lib.rs:61:13
   |
52 |         let mut path = frontier.pop_front().unwrap();
   |             -------- help: consider changing this to be a mutable reference: `&mut Vec<(u8, u8, Move)>`
...
61 |             path.push(state.clone());
   |             ^^^^^^^^^^^^^^^^^^^^^^^^ `path` is a `&` reference, so the data it refers to cannot be borrowed as mutable

You can't mutate through immutable references. Instead of putting references in the VecDeque, put the actual value itself into it.

1 Like

Changing to frontier.push_back(&mut start) gives a different error:

warning: variable does not need to be mutable
  --> src/lib.rs:52:13
   |
52 |         let mut path = frontier.pop_front().unwrap();
   |             ----^^^^
   |             |
   |             help: remove this `mut`
   |
   = note: `#[warn(unused_mut)]` on by default

error[E0499]: cannot borrow `*path` as mutable more than once at a time
  --> src/lib.rs:61:13
   |
61 |             path.push(state.clone());
   |             ^^^^^^^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
...
66 |             frontier.push_back(path);
   |             ------------------------
   |             |                  |
   |             |                  first mutable borrow occurs here
   |             first borrow later used here

error[E0502]: cannot borrow `path` as immutable because it is also borrowed as mutable
  --> src/lib.rs:63:34
   |
63 |                 println!("{:?}", path);
   |                                  ^^^^
   |                                  |
   |                                  immutable borrow occurs here
   |                                  mutable borrow later used here
...
66 |             frontier.push_back(path);
   |                                ---- mutable borrow occurs here
   |
   = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0499]: cannot borrow `*path` as mutable more than once at a time
  --> src/lib.rs:66:32
   |
66 |             frontier.push_back(path);
   |                                ^^^^ `*path` was mutably borrowed here in the previous iteration of the loop

Okay, well put an owned value instead of a reference.

1 Like

You mean copy the path every time I want to append to it? There's got to be a better way than that.

I mean this. There's no clone if you don't explicitly call clone.

frontier.push_back(start);
1 Like

Nope. Anything else you want me to try?

error[E0382]: borrow of moved value: `path`
  --> src/lib.rs:61:13
   |
52 |         let mut path = frontier.pop_front().unwrap();
   |             --------   ----------------------------- this reinitialization might get skipped
   |             |
   |             move occurs because `path` has type `Vec<(u8, u8, Move)>`, which does not implement the `Copy` trait
...
61 |             path.push(state.clone());
   |             ^^^^^^^^^^^^^^^^^^^^^^^^ value borrowed here after move
...
66 |             frontier.push_back(path);
   |                                ---- value moved here, in previous iteration of loop

I mean, if you're going to store multiple different versions of the path, you need several versions of the path.

You might be able to use a custom linked list with Rc to share parts of the path that are identical, but unless your paths are very long, I don't think it would be worth it.

1 Like

To answer literal question from your post's title: you can't. You can mutate &Mutex<Vec> or Arc<Mutex<Vec>>.

1 Like