I just came across this piece of code in base64 crate:
buffer.resize(decoded_len_estimate, 0);
let bytes_written;
{
let buffer_slice = &mut buffer.as_mut_slice()[starting_output_len..];
bytes_written = decode_helper(input_bytes, num_chunks, config, buffer_slice)?;
}
buffer.truncate(starting_output_len + bytes_written);
See https://github.com/marshallpierce/rust-base64/blob/b63975f0a3d2e724c611bf6cd7213ae6bcb065a3/src/decode.rs#L165-L169
What is the reason for using this style of declaring the variable bytes_written and then use this nested block. What problem does this solve? Why not just use this code:
buffer.resize(decoded_len_estimate, 0);
let buffer_slice = &mut buffer.as_mut_slice()[starting_output_len..];
let bytes_written = decode_helper(input_bytes, num_chunks, config, buffer_slice)?
buffer.truncate(starting_output_len + bytes_written);
Can someone help me with that?
This code was written 4 years ago (5 Dec 2017), before NLL (Non-Lexical Lifetimes) arrived (6 Dec 2018). With NLL, the borrow checker consider a reference "alive" until its last use and no more. But without it, a reference is considered alive until the end of the scope (its drop). Since buffer_slice borrows buffer mutably, and the last line does that too, the borrow checker pre-NLL would err - and that's probably why they introduced the block, to create a new scope for buffer_slice so it will be dropped before the next borrow of buffer.
Related
So I'm pursuing my Rust adventures (loving it) and I'm exploring threads. As usual I stumbled upon an error that I do not understand.
Here is a minimal example:
use std::thread;
pub fn compute_something(input: &Vec<&usize>) -> usize {
input.iter().map(|v| *v).sum()
}
pub fn main() {
let items = vec![0, 1, 2, 3, 4, 5];
let mut slice: Vec<&usize> = Vec::new();
slice.push(&items[1]); // borrowed value does not live long enough
// argument requires that `items` is borrowed for `'static`
slice.push(&items[2]); // borrowed value does not live long enough
// argument requires that `items` is borrowed for `'static`
assert_eq!(3, compute_something(&slice));
let h = thread::spawn(move || compute_something(&slice));
match h.join() {
Ok(result) => println!("Result: {:?}", result),
Err(e) => println!("Nope: {:?}", e)
}
} // `items` dropped here while still borrowed
I have of course made a playground to illustrate.
If I drop the thread part (everything after the assert_eq! line) and just call compute_something(&slice) it compiles fine.
There are three main things I don't understand here:
Why is it a problem to drop items while borrowed at the end of the program, shouldn't the runtime clean-up the memory just fine? It's not like I'm gonna be able to access slice outside of main.
What is still borrowing items at the end of the program? slice? If so, why does that same program compile by just removing everything after the assert_eq! line? I can't see how it changes the borrowing pattern.
Why is calling compute_something from inside the thread's closure creating the issue and how do I solve it?
You move slice into the closure that you pass to thread::spawn(). Since the closure passed to thread::spawn() must be 'static, this implies that the vector being moved into the closure must not borrow anything that isn't 'static either. The compiler therefore deduces the type of slice to be Vec<&'static usize>.
But it does borrow something that's not 'static -- the values that you try to push into it borrow from a variable local to main(), and so the compiler complains about this.
The simplest way to fix this case is to have slice be a Vec<usize> and not borrow from items at all.
Another option is to use scoped threads from the crossbeam crate, which know how to borrow from local variables safely by enforcing that all threads are joined before a scope ends.
To directly answer the questions you posed:
Why is it a problem to drop items while borrowed at the end of the program, shouldn't the runtime clean-up the memory just fine?
When main() terminates, all threads are also terminated -- however, there is a brief window of time during which the values local to main() have been destroyed but before the threads are terminated. There can exist dangling references during this window, and that violates Rust's memory safety model. This is why thread::spawn() requires a 'static closure.
Even though you join the thread yourself, the borrow checker doesn't know that joining the thread ends the borrow. (This is the problem that crossbeam's scoped threads solve.)
What is still borrowing items at the end of the program?
The vector that was moved into the closure is still borrowing items.
Why is calling compute_something from inside the thread's closure creating the issue and how do I solve it?
Calling this function isn't creating the issue. Moving slice into the closure is creating the issue.
Here is the way I solved this issue.
I used Box::Leak: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.leak
let boxed_data = data.into_boxed_slice();
let boxed_data_static_ref = Box::leak(boxed_data);
let compressed_data = &boxed_data_static_ref[start_of_data..start_of_data+zfr.compressed_size as usize];
let handles = (0..NUM_THREADS).map(|thread_nr| {
thread::spawn(move || {
main2(thread_nr, compressed_data, zfr.crc);
})
}).collect::<Vec<_>>();
for h in handles {
h.join().unwrap();
}
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.
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.
This question already has answers here:
What does "cannot move out of index of" mean?
(2 answers)
Closed 6 years ago.
I'm very new to rust, and am starting to get the hang of the ownership system and such, but am still having some hangups. For exmaple, I have the following code:
fn main() {
let mut t = vec![Box::new(4)];
let mut o = t[0];
*o = *o + 1;
t[0] = o;
println!("t[0]:{}", t[0]);
}
Which gives me the cannot move out of indexed content error for the line where I'm initializing o. I understand why this is happening, I think, but I can't figure out what I'm supposed to do instead to accomplish the same thing. This is a very simplified case, but any help would be greatly appreciated. Thanks!
The expression t[0] is equivalent to either *t.index(0) or *t.index_mut(0), based on context. These methods return an immutable reference and a mutable reference, respectively. The indexing operator automatically dereferences these.
Since you have a vector of Box<i32>, dereferencing is not valid, because that would try to move the value from the vector. But then, what do you put in the vector in its place?
Instead of trying to move the value, you need to use a reference to the value instead. Also, if you want to be able to add 1 to the value, you need a reference to the value, not a reference to the Box. You can do this by first dereferencing the box, then by taking a mutable reference to the result:
fn main() {
let mut t = vec![Box::new(4)];
{
let o = &mut *t[0];
*o = *o + 1;
}
println!("t[0]:{}", &t[0]);
}
I had to add a block here to make the mutable borrow end before the println!, otherwise the compiler complained with:
error: cannot borrow `t` as immutable because it is also borrowed as mutable
Also, notice how we don't need to put the updated value back in the vector, because we changed the value in the vector directly by using a reference to it.
I'm strugglin' with the borrow checker - wonder o' wonder.
While I found a solution by adding a block, I am curious if there are other ways to end a mutable borrow so the next statement can access a binding afterwards.
Here's what I did so far:
let mut canvas: Canvas = Canvas {
width: 5,
height: 5,
array: vec!['x'; 5*5],
};
{
let mut renderer: CanvasRenderer = CanvasRenderer::new(&mut canvas);
renderer.render_point('x', 3, 3);
}
println!("The Value in the array is: {}", canvas.array[9]);
I wrap a block around the binding of a CanvasRenderer object and after mutating the canvas and the scope ends, the CanvasRenderer dies and my mutable borrowed canvas is available to be read or whatever.
This works - but now I'd like to see other solutions!
I heard about drop(stuff) but it did not work as I thought it should.
There is no other way; using blocks is the way to do it. Before Rust 2018 (available in Rust 1.31) all borrows are lexical, that is, they always correspond to some lexical scope. The only scope which is larger than a single statement is that of a block, so blocks are your only tool to limit borrow scopes.
drop() would not work for two reasons: first, because it would require non-lexical scope which is not supported before Rust 2018, and second, it cannot be a general-purpose tool for managing borrows: for example, it wouldn't be able to end an immutable borrow simply because immutable references are Copy and can't be "dropped".
See also:
Moved variable still borrowing after calling `drop`?
What are non-lexical lifetimes?