Modify vector element in place and use previous value

Is it possible to modify a vector element in place and use a value from the existing element that doesn't implement Copy and do it without cloning?

Playground

enum Message {
    A,
    B(String),
    C(String),
}

let mut v = vec![
    Message::A,
    Message::B("test".to_owned()),
    Message::A,
    Message::A,
];

if let m @ Message::B(msg) = &mut v[1] {
    *m = Message::C(*msg);
}

This fails

error: borrow of moved value
  --> src/main.rs:15:12
   |
15 |     if let m @ Message::B(msg) = &mut v[1] {
   |            -^^^^^^^^^^^^^^---^
   |            |              |
   |            |              value borrowed here after move
   |            value moved into `m` here
   |            move occurs because `m` has type `&mut Message` which does not implement the `Copy` trait

error[E0382]: borrow of moved value
  --> src/main.rs:15:27
   |
15 |     if let m @ Message::B(msg) = &mut v[1] {
   |            ---------------^^^-   --------- move occurs because value has type `&mut Message`, which does not implement the `Copy` trait
   |            |              |
   |            |              value borrowed here after move
   |            value moved here

error[E0507]: cannot move out of `*msg` which is behind a mutable reference
  --> src/main.rs:16:25
   |
16 |         *m = Message::C(*msg);
   |                         ^^^^ move occurs because `*msg` has type `String`, which does not implement the `Copy` trait

You can temporarily move from v[1] if you leave something there in its place:

if let Message::B(msg) = std::mem::replace(&mut v[1], Message::A) {
    v[1] = Message::C(msg);
}

Playground

There's also std::mem::take if your type implements Default.

You can also just take the string:


if let Message::B(msg) = &mut v[1] {
    v[1] = Message::C(std::mem::take(msg));
}
4 Likes

If your type has no suitable placeholder value, you can also use Vec::swap_remove() for this (Rust Playground):

if let Message::B(msg) = v.swap_remove(1) {
    v.push(Message::C(msg));
    let end = v.len() - 1;
    v.swap(1, end);
}
5 Likes

If you don't want to do the swap , you can look into https://crates.io/crates/replace_with or https://crates.io/crates/take_mut

4 Likes