I am fairly new to Rust and encountered some beginner problems and want to share my solution with others around the internet that I haven't found in existing threads. I wonder if there is a more Rust approach to it.
I want to print out the height, width and area of a Rectangle.
struct Rectangle {
height: u32,
width: u32,
area: u32,
}
impl Rectangle{
fn area(& mut self) -> u32 {
self.area = self.height * self.width;
return self.area
}
}
fn main() {
let mut rect1 = Rectangle {height: 20, width: 30, area: 0};
println!("Rectangle has height {} width {} and area {}", rect1.height, rect1.width, rect1.area());
}
which gives me the error cannot borrow as immutable because it is also borrowed as mutable
error[E0502]: cannot borrow `rect1` as mutable because it is also borrowed as immutable
--> src/main.rs:17:88
|
17 | println!("Rectangle has height {} width {} and area {}", rect1.height, rect1.width, rect1.area());
| -----------------------------------------------------------------------------------^^^^^^^^^^^^--
| | | |
| | | mutable borrow occurs here
| | immutable borrow occurs here
| immutable borrow later used here
My solution
println!("Rectangle has height {1} width {2} and area {0}", rect1.area(), rect1.height, rect1.width);
changing the order in the println! statement.
I know that you cannot have an immutable and mutable reference at the same time, because the immutable reference does not expect the values to be changed. See here. But why does my solution work? Clearly, there still is a mutable and immutable reference in the same println! statement but with changed order.
But why does my solution work? Clearly, there still is a mutable and immutable reference in the same println! statement but with changed order.
Nope! The thing is area() requires a mutable borrow but does not keep one, since it returns an owned value (u32) rather than a borrowed one (&u32), so the borrow only lasts for the extent of the call and is released once the call returns.
Now you could expect the same of height and width, the trap is that println! implicitly borrows its parameters, so when you println!("{}", rect.height) it compiles to something along the lines of Arguments("", &rect.height), creating a borrow until the end of the formatting process.
Now because the borrow is implicit you can't deref' the attribute (*rec.height) as that would be &*rec.height where rec.height is still a u8, however there is an other method which is block expressions:
Blocks are always value expressions and evaluate the last expression in value expression context. This can be used to force moving a value if really needed.
This means that &{rec.height} will first copy (or move) the value out of the structure, then borrow that copy. Therefore you can also fix the call by writing:
println!("Rectangle has height {} width {} and area {}", {rect1.height}, {rect1.width}, rect1.area());
That will copy the two attributes out first, then println! will implicitly borrow them, but it won't need to borrow the structure itself leading to three non-overlapping borrows.
In that case, you may want to add a comment to explain why you're doing that as it's... odd.
Then again, I think your area is an anti-pattern, so YMMV.
Related
This question already has an answer here:
Rust not allowing mutable borrow when splitting properly
(1 answer)
Closed 9 months ago.
Consider these two code examples
struct A {
v: Vec<usize>,
a: usize,
}
impl A {
fn f(&mut self) {
for _ in self.v.iter_mut() {
self.f_mut();
}
}
fn f_mut(&mut self) {
self.a += 1;
}
}
which produces the compile error
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/binaries/main.rs:9:13
|
8 | for _ in self.v.iter_mut() {
| -----------------
| |
| first mutable borrow occurs here
| first borrow later used here
9 | self.f_mut();
| ^^^^ second mutable borrow occurs here
error: aborting due to previous error
whereas this code
struct A {
v: Vec<usize>,
a: usize,
}
impl A {
fn f(&mut self) {
for _ in self.v.iter_mut() {
self.a += 1;
}
}
fn f_mut(&mut self) {
self.a += 1;
}
}
compiles just fine. Obviously these two pieces of code have identical behaviour so I am wondering if there is some way to get Rust to accept the first solution?
In my project I have some quite large for loops which I would like to refactor by splitting the body of the for-loop into separate function calls, but Rust doesn't allow me to do it.
That is, my more complicated code structured as example 2 compiles fine, but refactored into easier digestable functions as example 1 gives me the cannot borrow *self as mutable more than once at a time error.
Your second example works because it performs a splitting borrow. The compiler can see that you're only borrowing self.v mutably, so it lets you borrow other fields on self mutably as well, since they don't overlap.
However, in the first example, self.f_mut() requires borrowing all of *self mutably (because it takes &mut self), which overlaps with the borrow of self.v, and is therefore disallowed. This requires borrowing the whole value because the borrow checker only uses the signature of the called function -- it does not look into the function to see that it doesn't use self.v. (If it did look into functions, then changing the body of a function without changing its signature could be an API-breaking change!)
Simply put, a function call is a "hard" boundary for the borrow checker. By using a function call in the first example, the detail that only self.a is used is hidden from the borrow checker.
I'm getting
error[E0373]: closure may outlive the current function, but it borrows row_nr, which is owned by the current function
I would not expect this, since row_nr is a u32, so I'd expect it to be copied rather than moved:
fn get_neighbours(values: &Vec<Vec<u32>>, row: usize, col: usize) -> Vec<u32> {
vec![
values.get(row - 1).and_then(|cols| cols.get(col)),
values.get(row).and_then(|cols| cols.get(col - 1)),
values.get(row).and_then(|cols| cols.get(col + 1)),
values.get(row + 1).and_then(|cols| cols.get(col)),
].into_iter().filter_map(|value_opt| value_opt.map(|value| *value)).collect()
}
fn get_points(values: Vec<Vec<u32>>) -> Vec<(u32, Vec<u32>)> {
values
.iter()
.enumerate()
.flat_map(|(row_nr, columns)| {
columns.iter().enumerate().map(|(column_nr, height)| {
let neighbours = get_neighbours(&values, row_nr, column_nr);
(*height, neighbours)
})
}).collect()
}
Full error message:
--> src/lib.rs:16:44
|
16 | columns.iter().enumerate().map(|(column_nr, height)| {
| ^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `row_nr`
17 | let neighbours = get_neighbours(&values, row_nr, column_nr);
| ------ `row_nr` is borrowed here
|
note: closure is returned here
--> src/lib.rs:16:13
|
16 | / columns.iter().enumerate().map(|(column_nr, height)| {
17 | | let neighbours = get_neighbours(&values, row_nr, column_nr);
18 | | (*height, neighbours)
19 | | })
| |______________^
help: to force the closure to take ownership of `row_nr` (and any other referenced variables), use the `move` keyword
|
16 | columns.iter().enumerate().map(move |(column_nr, height)| {
| ++++
See also on the Rust playground.
Now, the error suggest using move, but that becomes problematic because I'd only like to pass a reference to the values Vec (which I don't think should outlive the closure, because get_neighbours takes u32s from that Vec and thus should make copies too, I think?).
I presume I'm misunderstanding lifetimes here. I'd appreciate any insights on what I'm misinterpreting. I looked at a couple of related questions, but they seem to either resolve it using a move (which, as mentioned above, I think I should (be able to) avoid?), or have multiple borrow checker issues, making it hard for me to understand which applies ones to me (e.g. this answer).
I would not expect this, since row_nr is a u32, so I'd expect it to be copied rather than moved
This expectation is correct, assuming it were moved in the first place. In this case it's not moved, it's borrowed, because by default closures borrow values from their environment. To request a move, which will for u32 indeed result in a copy, you need to use the move keyword explicitly.
As you discovered, when you just use move, you also move values, which doesn't compile and is anyway not what you want. The standard Rust idiom to move only one value into the closure is to use move and explicitly borrow the captured variables you don't want moved (in yout case values). For example, this compiles:
.flat_map(|(row_nr, columns)| {
columns.iter().enumerate().map({
let values = &values;
move |(column_nr, height)| {
let neighbours = get_neighbours(values, row_nr, column_nr);
(*height, neighbours)
}
})
})
Playground
As for your titular question of why may a closure outlive the current function: Note that the "current function" in the error message refers to the outer closure, the one passed to flat_map(). Since the inner closure becomes part of the iterator returned by map(), and the outer closure immediately returns that iterator, it is trivially true that the inner closure does outlive the "current function" and that it cannot be allowed to borrow either row_nr or columns. (But borrowing values is perfectly fine because values outlives both closures.)
from the borrowing rule:
At any given time, you can have either one mutable reference or any number of immutable references.
Below, I have a mutable borrow of the struct Foo (the whole struct), which means I borrow every field of this struct. However, I can have another borrow to its field in demo function. And I suspect I have 2 mutable references to x.a:
#[derive(Debug)]
struct Foo {
a: i32,
b: i32,
}
fn demo(foo: &mut Foo) {
// the `foo` is mutable borrow
// should have exclusive access to all elements of Foo instance
// However,
let bar = &mut foo.a; // second ref to `x.a`
*bar += 1;
let baz = &foo.b;
println!("{:?}", bar);
println!("{:?}", baz);
}
fn main() {
let mut x = Foo { a: 10, b: 1 };
let foo = &mut x; // ref to x, implies borrowing x.a and x.b
demo(foo);
}
I know we can have disjoint mutable borrow to a struct binding (split borrow reference), but I'm not sure whether splitting the reference to a struct violates the borrow rule.
clarification: above code can compile. My confusion is that it should not compile due to the aforementioned reason. (playground)
I found this link relevant.
When you are borrowing a structure mutably, you are then free to borrow any of the sub-elements mutably.
But I can't find any doc to support it.
The line
let bar = &mut foo.a;
creates a reborrow of the a field of foo. As long as the reborrow is alive, the field can't be accessed via foo.a anymore, but only via bar. After the last use of bar, you can use foo.a again – once the reborrow is released, the original borrow is available again.
Reborrowing is very similar to borrowing itself. While a value is borrowed, you cannot access the original value until the borrow is released. The same happens when reborrowing. This makes sure that at any given time there is only a single active mutable borrow, and the borrowing rules are upheld.
The fact that you are dealing with a struct here is incidental. You can do the same with a mutable borrow to any value, e.g. after
let mut i = 17;
let borrow = &mut i;
let reborrow = &mut *borrow;
*reborrow += 4;
*borrow += 21;
the value of i is 42. This code looks like there are multiple active mutable borrows to i. However, at any given time only one of them can be used. The reborrow is released after it is last used, so the original borrow becomes usable again. If we swap the last two lines in this code, it won't compile anymore, since the reborrow would still be alive when borrow is accessed, which is not allowed.
Unfortunately, the mechanics of reborrowing aren't currently documented in the Rust reference. A related, also not well documented topic are implicit reborrows that happen when a mutable reference is bound to a variable of a type that is already known to be a mutable reference.
This question already has answers here:
Cannot borrow `x` as mutable more than once at a time
(2 answers)
cannot borrow `rdr` as mutable more than once at a time [when using library from cargo]
(1 answer)
Why do I get the error "cannot borrow x as mutable more than once"?
(2 answers)
Closed 3 years ago.
I am learning Rust by exercise. In this file the goal is to update cells as in a spreadsheet: when a value changes, all cells that are derived from it have to be recalculated. Here, those are called that cell's parents.
Updating a cell value proves no problem, but updating the parents gets me fighting the borrow checker. When I have retrieved the cell from a HashMap and updated the value, I no longer need a mutable reference - so I tried wrapping it in an immutable reference instead. That way I only have to find it once.
But it seems Rust figures since I originally got my immutable reference from a borrowed &mut self, it must still be tied to it. This obviously prevents me from reusing self a second time.
use std::collections::HashMap;
use std::vec::Vec;
struct Cell {
value: i32,
parents: Vec<u32>,
}
pub struct Sheet {
table: HashMap<u32, Cell>,
}
impl Sheet {
pub fn set_value(&mut self, identifier: u32, new_value: i32) {
let mut updated_cell: Option<&Cell> = None;
if let Some(cell) = self.table.get_mut(&identifier) {
let Cell { value, .. } = cell;
*value = new_value;
updated_cell = Some(cell);
}
if let Some(cell) = updated_cell {
recalculate(self, &cell.parents);
}
}
}
fn recalculate(_sheet: &mut Sheet, _cells: &[u32]) {}
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/lib.rs:20:16
|
16 | if let Some(cell) = self.table.get_mut(&identifier) {
| ---------- first mutable borrow occurs here
...
22 | recalculate(self, &cell.parents);
| ^^^^ ------------- first borrow later used here
| |
| second mutable borrow occurs here
I want to know if there is a solution which avoids searching a second time or taking an unnecessary vector copy. I have tried to adjust the code many times, but not all of the syntax is yet clear to me yet.
Rust is protecting you from a potentially dangerous situation. There is nothing in recalculate's signature to guarantee that it won't mutate sheet in such at way that the references in cells become invalid. For example recalculate could delete some cells and then the references in cell.parents would be dangling pointers.
You probably need to pass a clone of the parent cells instead:
if let Some(cell) = updated_cell {
let parents = cell.parents.clone();
recalculate(self, &parents);
}
Alternatively, you may need to consider a different data model, which separates the mutability of individual cells from the mutability of the overall structure. For example, you could wrap cells in a std::cell::Cell or std::cell::RefCell, and pass an immutable reference to the Sheet.
I'm using tcod-rs. Each method used to draw to the RootConsole takes a mutable reference. The central loop is a while loop that waits for the window to close, and clears the screen, draws, and then flushes. The "check for window close" method also takes a mutable reference. clear and flush do too. I have a custom drawing function that needs a mutable reference so it can draw:
fn draw(root: &mut RootConsole) { /* Do state-y stuff */ }
while !root.window_closed() /* Mutable borrow 1 */ {
root.clear(); // Mutable borrow 2
draw(root); // Mutable borrow 3
root.flush(); // Mutable borrow 4
}
The compiler has a nervous breakdown screaming about aliens stealing RootConsoles. I've managed to cut it down to one error by just making the changes it suggests. I can't change the tcod-rs library, or at least, I'd rather not, and I would prefer to keep using it. Replacing the draw(root) function with a root.put_char(...) for instance, works.
Here is the error I get on attempting to cargo run. The tcod::console::Root type has documentation here.
error[E0596]: cannot borrow immutable argument `root` as mutable
--> src/draw/mod.rs:27:33
|
9 | pub fn draw_map(root: &mut RootConsole, world: WorldState) {
| ---- consider changing this to `mut root`
...
27 | .draw_char(&mut root,
| ^^^^ cannot borrow mutably
My actual draw_map code is pretty simple:
pub trait DrawChar {
fn draw_char(self, root: &mut RootConsole, pos: (i32, i32));
}
pub fn draw_map(root: &mut RootConsole, world: WorldState) {
let screen_start_y = world.screen.1;
let screen_end_y = screen_start_y + root.height();
let screen_start_x = world.screen.0;
let screen_end_x = screen_start_x + root.width();
let x = screen_start_x;
let y = screen_start_y;
for scan_line in &world.map[screen_start_y as usize..
screen_end_y as usize]
{
y += 1;
for unit in &scan_line[screen_start_x as usize..
screen_end_x as usize]
{
x += 1;
unit.tiles[world.level as usize]
.draw_char(&mut root,
(x as i32 - screen_start_x,
y as i32 - screen_start_y));
}
}
}
Each tile implements DrawChar, which, since it uses functions from tcod-rs on a root, requires a mutable reference. My main loop isn't much different from the while loop above, except that it handles key commands using some native functions.
The problem is at the draw_char call in draw_map. root is already a mutable reference to a RootConsole; you are trying to pass to draw_char a mutable reference to root, which essentially becomes a mutable reference to a mutable reference to a RootConsole. Simply pass root as the parameter and you are done.