How to stop error "cannot borrow as mutable more than once at a time"?

I almost have a way to access a struct &mut reference var so I can use it throughout an app for decisions, but when I swap reference assignments in the following code:

pub struct X {
    pub first_pos_tuple_vec: Vec<(f64, f64)>,
    pub second_pos_tuple_vec: Vec<(f64, f64)>,
}

impl X {
    pub fn new() -> X {
        X {
            first_pos_tuple_vec: Vec::new(),
            second_pos_tuple_vec: Vec::new(),
        }
    }
}

pub struct References<'a> {
    pub selected_pos_tuple_vec: Option<&'a mut Vec<(f64, f64)>>,
}

impl<'a> References<'a> {
    pub fn new() -> References<'static> {
        References {
            selected_pos_tuple_vec: None,
        }
    }
}

fn main() {
    let mut x = X::new();
    x.first_pos_tuple_vec.push((3.0, 4.0));
    x.first_pos_tuple_vec.push((5.0, 6.0));

    x.second_pos_tuple_vec.push((7.0, 8.0));
    x.second_pos_tuple_vec.push((9.0, 10.0));
    x.second_pos_tuple_vec.push((11.0, 12.0));

    let mut refs = References::new();

    refs.selected_pos_tuple_vec = Some(&mut x.second_pos_tuple_vec);
    println!(
        "refs.selected_pos_tuple_vec = {:?}",
        refs.selected_pos_tuple_vec
    );

    refs.selected_pos_tuple_vec = Some(&mut x.first_pos_tuple_vec);
    println!(
        "refs.selected_pos_tuple_vec = {:?}",
        refs.selected_pos_tuple_vec
    );

    if let Some(tuplevec) = refs.selected_pos_tuple_vec {
        println!("tuplevec = {:?}", tuplevec[0].0);
    }

    refs.selected_pos_tuple_vec = Some(&mut x.second_pos_tuple_vec);
    if let Some(tuplevec) = refs.selected_pos_tuple_vec {
        println!("tuplevec = {:?}", tuplevec[0].0);
    }
}

this error is produced:

error[E0499]: cannot borrow `x.second_pos_tuple_vec` as mutable more than once at a time
  --> src/main.rs:54:40
   |
38 |     refs.selected_pos_tuple_vec = Some(&mut x.second_pos_tuple_vec);
   |                                        --------------------------- first mutable borrow occurs here
...
54 |     refs.selected_pos_tuple_vec = Some(&mut x.second_pos_tuple_vec);
   |     -----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^-
   |     |                                  |
   |     |                                  second mutable borrow occurs here
   |     first borrow later used here

How do I get around this?

Hard to know what the real problem is to suggest what can be done. Couple simple(ish) ones for immediate error;

Use a new variable. Even refs = References::new(); without let gives same; (Thanks to Non Lexical Lifetimes.)

Keep the old then reuse.

let old = std::mem::replace(&mut refs.selected_pos_tuple_vec, Some(&mut x.first_pos_tuple_vec));
...
refs.selected_pos_tuple_vec = old;

Note refs does not have type parameter with 'static. Variance changes the one coming from new().

this is not an error the compiler is preventing you to have two valid mutable references of the same thing
first time you get the tuplevec in if let Some(tupevec) you get the first reference so if compiler lets you get another reference that would make 2 mutable references to the same object then you could possibly mutate those two variables referencing to the same object differently which is a data race

Have you considered using map or and_then to chain your operations instead of using relatively global state check variables ?

@jonh Maybe you're intending to use "old" in a way I don't understand.
When I try to use your suggestion, I get the same error as before; just kicked the can down the road. The point is to have second_pos_tuple_vec or first_pos_tuple_vec assigned to selected_pos_tuple_vec whenever needed (swappable). Could you show me how I would use your suggestion? when I do:

refs.selected_pos_tuple_vec = Some(&mut x.second_pos_tuple_vec);

// replace second with first
let mut old = std::mem::replace(
    &mut refs.selected_pos_tuple_vec,
    Some(&mut x.first_pos_tuple_vec),
);

// replace first with second
old = std::mem::replace(
    &mut refs.selected_pos_tuple_vec,
    Some(&mut x.second_pos_tuple_vec),
);

Same error occurs.

In your last bit of code, the variable old contains the exclusive reference to the second_pos. So what @jonh was saying was to just reuse that variable if you want to reassign to reference of second_pos. So something like

old = std::mem::replace(
    &mut refs.selected_pos_tuple_vec,
    Some(&mut old),
);

Which isn't all that elegant. I think you're mostly in a situation where the borrow checked doesn't understand that what you're doing is safe. Here's another option. Still a bit awkward, but it runs. Not quite sure what you're trying to accomplish with your structures, but I think there might be a more ergonomic structure.

Playground

@drewkett You're so right about my probably not using the most ergonomic structure for my code (meaning code design). I have a situation where one function decides which vector of tuples (located as fields in structure Y so they are accessible throughout the app; like global vars almost ) to use elsewhere in the app; so setting a reference var (field in structure Z) so the rest of the app can access the selected vector of tuples seemed ok. Can you recommend a better approach?

[ On a side note, how do you get the Rust Playground window to retain your code?

Every time I duplicate a tab in a Chrome browser, my code gets wiped in the duplicated tab, so I copy and paste. ]

@NikosEfthias Could you please give me an example of how I would use either map or and_then in this situation?

The problem you’ll eventually run into here is that the compiler will hold an exclusive reference to Y for as long as the reference stored in Z exists, so that changes to Z don’t happen when other code isn’t expecting it to. You probably want to use interior mutability with something like

pub struct X {
    pub first_pos_tuple_vec: RefCell<Vec<(f64, f64)>>,
    pub second_pos_tuple_vec: RefCell<Vec<(f64, f64)>>,
}

pub struct References<'a> {
    pub selected_pos_tuple_vec: Option<&'a RefCell<Vec<(f64, f64)>>>,
}

To get a bit more technical about how your code here is failing, References::new() is explicitly producing a References<‘static>. That ’static tells the compiler that any reference you store in selected_pos_tuple_vec needs to be valid until the end of the program, which means the compiler will prevent you from ever taking another reference to the same object.

In general, if you’re going to be swapping around references like this, the compiler will insist that they can all exist at the same time because it doesn’t know how to keep track of the individually.


The “Share” button at the top will give you a permalink that brings up your code.

2 Likes

@2e71828 I wasn't considering using RefCell since it is touted as being a "bad" or unsafe thing to do. Additionally, my app will eventually be using multiple threads for channels and TCP communication with a server. RefCell, as far as I know, does not work well when multiple threads are present. Are you implying RefCell is ok to use in this case?

I’d be interested in seeing where you got this idea from. RefCell is one of the main ownership-management primitives in Rust (for single-threaded programs). It’s in the standard library for a reason, and is as safe as the rest.

Sure, it can cause runtime errors when misused. That’s the same as anything else: indexing a Vec can cause runtime errors just as easily.

@2e71828 Thank you for the clarity! I my previous impressions from articles like this: https://users.rust-lang.org/t/why-do-all-docs-say-refcell-is-bad/37086

1 Like

@drewkett I like what is in the playground best, but for educational purposes, I tried the "old" statement suggested and I got this error with the following code and I'm not sure how to fix it:

pub struct X {
    pub first_pos_tuple_vec: Vec<(f64, f64)>,
    pub second_pos_tuple_vec: Vec<(f64, f64)>,
}

impl X {
    pub fn new() -> X {
        X {
            first_pos_tuple_vec: Vec::new(),
            second_pos_tuple_vec: Vec::new(),
        }
    }
}

pub struct References<'a> {
    pub selected_pos_tuple_vec: Option<&'a mut Vec<(f64, f64)>>,
}

impl<'a> References<'a> {
    pub fn new() -> References<'a> {
        // References<'static> {
        References {
            selected_pos_tuple_vec: None,
        }
    }
}

fn main() {
    let mut x = X::new();
    x.first_pos_tuple_vec.push((3.0, 4.0));
    x.first_pos_tuple_vec.push((5.0, 6.0));

    x.second_pos_tuple_vec.push((7.0, 8.0));
    x.second_pos_tuple_vec.push((9.0, 10.0));
    x.second_pos_tuple_vec.push((11.0, 12.0));

    let mut refs = References::new();

    refs.selected_pos_tuple_vec = Some(&mut x.second_pos_tuple_vec);
    println!(
        "for second.... refs.selected_pos_tuple_vec = {:?}",
        refs.selected_pos_tuple_vec
    );

    let mut _old = std::mem::replace(
        &mut refs.selected_pos_tuple_vec,
        Some(&mut x.first_pos_tuple_vec),
    );

    println!("for right after let old.... old = {:?}", _old);
    println!(
        "replace second with first.... refs.selected_pos_tuple_vec = {:?}",
        refs.selected_pos_tuple_vec
    );

    _old = std::mem::replace(&mut refs.selected_pos_tuple_vec, Some(&mut _old));

    println!(
        "replace first with second.... refs.selected_pos_tuple_vec = {:?}",
        refs.selected_pos_tuple_vec
    );

    if let Some(tuplevec) = refs.selected_pos_tuple_vec {
        println!("tuplevec = {:?}", tuplevec[0].0);
    }
}
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/main.rs:56:69
   |
56 |     _old = std::mem::replace(&mut refs.selected_pos_tuple_vec, Some(&mut _old));
   |                                                                     ^^^^^^^^^ expected struct `std::vec::Vec`, found enum `std::option::Option`
   |
   = note: expected mutable reference `&mut std::vec::Vec<(f64, f64)>`
              found mutable reference `&mut std::option::Option<&mut std::vec::Vec<(f64, f64)>>`

It’s true that many problems are more cleanly solved by a different strategy than using RefCells. On the other hand, the RefCell solutions are also valid and correct.

Determining which one is best in any given situation requires predicting what your future needs will be and guessing which will be less problematic in the long term. This sort of lesson only really comes from direct experience— Don’t let advice scare you off from trying things, but keep it in mind if you encounter problems.


At a glance, it looks like _old is already an Option<_>, so you probably don’t need to wrap it with Some().

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.