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:
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
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),
);
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.
@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. ]
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
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.
@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.
@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);
}
}
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().