Can't change element in vector [closed] - rust

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 9 months ago.
Improve this question
I found this answer to a related question, but it does not help me. According to my understanding the following code should work:
container_options is not just a mutable reference, it is owned. I should be able to extract a mutable reference to it or even just edit it directly, but the compiler refuses to accept anything I tried. I have no idea where the problem is and hope that someone else does.
Edit: I kinda found a workaround, so this is solved.
fn update_container_options_with_paths(
container_options: Vec<ContainerOption>,
path_options: &Vec<Rc<PathOption>>,
) {
for container_index in 0..container_options.len() {
let container_option = &mut container_options[container_index];
container_option.compatible_path_options = 0;
for (path_index, path_option) in path_options.iter().enumerate() {
if PathOption::decode_loading_mask(
path_option.summary.compatible_container_options,
container_index,
) {
container_option.set_compatible(path_index);
}
}
}
}

I should be able to extract a mutable reference to it or even just edit it directly, but the compiler refuses to accept anything I tried. I have no idea where the problem is and hope that someone else does.
The compiler tells you exactly what the issue is and how to fix it:
error[E0596]: cannot borrow `container_options` as mutable, as it is not declared as mutable
--> src/lib.rs:5:37
|
3 | fn update_container_options_with_paths(container_options: Vec<()>, path_options: &Vec<Rc<()>>) {
| ----------------- help: consider changing this to be mutable: `mut container_options`
4 | for container_index in 0..container_options.len() {
5 | let container_option = &mut container_options[container_index];
| ^^^^^^^^^^^^^^^^^ cannot borrow as mutable
For more information about this error, try `rustc --explain E0596`.
Even when something is owned, Rust still has a concept of mutable v non-mutable bindings, and you can only get a mutable reference from a mutable binding.
This is mostly indicative as you can trivially change it e.g.
let mut a = a;
but it's still non optional.
And I don't understand the purpose of indexing into container_options, why not just container_options.iter_mut() or (iterating on) &mut container_options? It's not like it works any less than index_mut.

Related

Rust: second mutable borrow compiling and running. Why? [duplicate]

This question already has an answer here:
What are non-lexical lifetimes?
(1 answer)
Closed last year.
Folks,
I am writing some code to learn Rust.
I read about the rule that states you can have at most ONE mutable borrow for a variable going on simultaneously in the scope of your code.
Then, while I was writing some reference to myself, I stumbled on this:
fn main() {
let mut a = "Is this string immutable?".to_string();
println!("a: {}\n", a);
let b = &mut a;
b.push_str(" No, it's not.");
let c = &mut a;
// b.push_str(" Could we append more stuff here?");
println!("c: {}",c);
}
The weird situation is: this code works as is, even if I declared two mutable borrows.
BUT... If I comment out the second push_str() call, the compiler would start to complain about the second mutable borrow in the c variable declaration.
What am I missing here? Why is it running?
Thanks in advance.
The rule is that you cant have to mutable borrows which are active at the same time.
In your case this is not the case.
The borrow b just has to be alive until the line
b.push_str(" No, it's not.");.
After this you are free to borrow again, because b is never used again.
When outcommenting you extend the lifetime of b after your second borrow and you get an error.
The compiler is not always able to recognize this in more complicated situations, so it might fail to compile even if at no point there could be 2 mutable borrows.

Double mutable borrow in Rust `if let` statement [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
Why does the following code coed in Rust yell a double mutable borrow error?
I would expect the first scope to be the first block since the returned values can't outlive this block, and the else if won't be called if the first if returned Some(arr).
if let Some(arr) = v.as_array_mut(){
...
} else if let Some(obj) = v.as_object_mut(){
...
} else {
...
}
|
178 | if let Some(arr) = v.as_array_mut(){
| -------------------------- first mutable borrow occurs here
...
195 | } else if let Some(obj) = v.as_object_mut(){
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| second mutable borrow occurs here
| first borrow later used here
BTW, breaking each call to two calls, which provides an exact similar behavior obviously doesn't yell an error:
if v.is_array(){
let arr = v.as_array_mut().unwrap();
...
} else if v.is_object(){
let obj = v.as_object_mut().unwrap();
...
} else {
...
}
This has to do with non-lexical lifetimes.
Extend Rust's borrow system to support non-lexical lifetimes -- these are lifetimes that are based on the control-flow graph, rather than lexical scopes. The RFC describes in detail how to infer these new, more flexible regions, and also describes how to adjust our error messages. The RFC also describes a few other extensions to the borrow checker, the total effect of which is to eliminate many common cases where small, function-local code modifications would be required to pass the borrow check. (The appendix describes some of the remaining borrow-checker limitations that are not addressed by this RFC.)
1.36 has NLL turned on for the 2015 edition. The 2018 edition enables them by default.
So in your case, I guess you are on a version not supporting NLL.

Why are immutable borrows ok after mutable borrows in closures? [duplicate]

This question already has answers here:
What are non-lexical lifetimes?
(1 answer)
When should I add mut to closures?
(1 answer)
Closed 2 years ago.
While reading Steve Donovan's article Why Rust Closures are (Somewhat) Hard I stumbled upon this code snippet:
let mut x = 1.0;
let mut change_x = || x = 2.0;
change_x();
println!("{}", x);
Steve claims that the code should not compile because of this error:
previous borrow occurs due to use of x in closure
But when I run the code in the Rust Playground, the code works fine. It outputs "2".
I suppose that is because the closure change_x borrows the variable x mutably from the enclosing environment, but ...
Why does the immutable borrow in the println! macro work? x is mutably borrowed in the change_x closure defined above. Steve seems to suggest that this should be an error. Has the compiler been modified to handle this situation differently since the article was published?
The code doesn't compile when I remove the mut marker from change_x. Why?
Side note: the compiler version used by the Playground app is 1.48.0 at the time of posting this question.

Borrow checker and function arguments in Rust, correct or over zealous? [duplicate]

This question already has an answer here:
Cannot borrow as immutable because it is also borrowed as mutable in function arguments
(1 answer)
Closed 4 years ago.
When a mutable argument is passed as a function argument, the borrow checker doesn't allow this to be used to construct other arguments, even when those arguments clone values without holding a reference.
While assigning variables outside the function is always an option1, logically this seems over zealous and something the borrow checker could take into account.
Is this working as intended or something that should be resolved?
#[derive(Debug)]
struct SomeTest {
pub some_value: f64,
pub some_other: i64,
}
fn some_fn(var: &mut SomeTest, other: i64) {
println!("{:?}, {}", var, other);
}
fn main() {
let mut v = SomeTest { some_value: 1.0, some_other: 2 };
some_fn(&mut v, v.some_other + 1);
// However this works!
/*
{
let x = v.some_other + 1;
some_fn(&mut v, x);
}
*/
}
Gives this error:
--> src/main.rs:14:21
|
14 | some_fn(&mut v, v.some_other + 1);
| - ^^^^^^^^^^^^ use of borrowed `v`
| |
| borrow of `v` occurs here
See: playpen.
[1]: Even though one-off assignments do sometimes improve readability, being forced to use them for arguments encourages use of scopes to avoid single use variables polluting the name-space, causing function calls that would otherwise be one line - being enclosed in braces and defining variables... I'd like to avoid this if possible especially when the requirement seems like something the borrow checker could support.
This is an artifact of the current implementation of the borrow checker. It is a well known limitation, dating back to at least 2013, and no one is overjoyed by it.
Is this working as intended
Yes.
something that should be resolved?
Yes.
The magic keywords are "non-lexical lifetimes". Right now, lifetimes are lexical - they correspond to the blocks of source that we type. Ideally, foo.method(foo.mutable_method()) would see that the borrow ends "inside the parenthesis", but for a myriad of reasons, it is tied to the entire statement.
For tons of further information see RFC issue 811 and everything linked from there.

"cannot borrow as mutable more than once at a time" in if-else [duplicate]

This question already has answers here:
Why is a borrow still held in the else block of an if let?
(5 answers)
Closed 6 years ago.
I'm writing a program to calculate the frequency of word occurrence. This is a segment of my code.
// hm is a HashMap<&str, u32>
if let Some(val) = hm.get_mut(tt) {
*val += 1u32;
} else {
hm.insert(tt.clone(), 1u32);
}
And I got...
error: cannot borrow `hm` as mutable more than once at a time [E0499]
hm.insert(tt.clone(), 1u32);
^~
note: first mutable borrow occurs here
if let Some(val) = hm.get_mut(tt) {
^~
note: first borrow ends here
}
^
help: run `rustc --explain E0499` to see a detailed explanation
I can bypass this by moving hm.insert() out of else scope but it's kind of "un-programmatic" way... I tried using match but the same error (would obviously) happened.
How can I fix this?
This is a common problem with HashMaps in Rust: borrows can't have ragged edges. Fortunately, there is an API to handle this case.
You can use HashMap::entry() to get a place on a hash map, occupied or vacant and then use or_insert() to set a value for the key if there is not one already.
*hm.entry(tt).or_insert(0u32) += 1;
This returns a reference to the hm location, filling it with 0 if it wasn't there, then increments whatever it was.
That Rust's lifetimes are prone to gratuitous conflicts is not an unknown issue. Here is a Rust's Rust core team member discussing plans to tackle this in a future version of Rust. For now, though, there are library methods that work around it.

Resources