I have a struct UI
holding a mutable reference to Stdout
. Instead of mutating it on update, I'd prefer to replace it with an entirely new UI
:
use std::io::{stdout, Result, Stdout, Write};
struct UI<'s> {
stdout: &'s mut Stdout,
v: Box<()>, // If remove this field, the error goes away.
}
impl<'s> UI<'s> {
fn new(stdout: &'s mut Stdout) -> Result<Self> {
let ui = UI {
stdout: stdout,
v: Box::new(()),
};
Ok(ui)
}
fn write(&mut self) -> Result<()> {
write!(self.stdout, "heyyyyy")?;
self.stdout.flush()?;
Ok(())
}
}
fn main() -> Result<()> {
let mut stdout = stdout();
let mut ui = UI::new(&mut stdout)?;
ui = UI::new(&mut stdout)?;
ui.write()?; // If you comment this line out, the error goes away.
Ok(())
}
The borrow checker complains that stdout is borrowed mutably twice:
error[E0499]: cannot borrow `stdout` as mutable more than once at a time
--> src/main.rs:30:18
|
28 | let mut ui = UI::new(&mut stdout)?;
| ----------- first mutable borrow occurs here
29 |
30 | ui = UI::new(&mut stdout)?;
| -- ^^^^^^^^^^^ second mutable borrow occurs here
| |
| first borrow might be used here, when `ui` is dropped and runs the destructor for type `UI<'_>`
There are two weird behaviors:
- If I remove field
v
, the error goes away. - If I remove
ui.display()?
, the error also goes away.
What's the problem here?
Addressing @kmdreko's suggestion:
The first point is explained in the error message, albeit in a roundabout way if you don't know what's going on.
Box
implementsDrop
, so that it can deallocate its contents when destroyed; and thereforeUI
automatically implementsDrop
, which means there is code executed which could accessstdout
between reborrowing it for a newUI
and assigning it toui
.
Then why does this return an error?
fn main() -> Result<()> {
let mut stdout = stdout();
let mut ui = UI::new(&mut stdout)?;
for _ in 0..10 {
drop(ui);
ui = UI::new(&mut stdout)?;
ui.write()?;
}
Ok(())
}
error[E0499]: cannot borrow `stdout` as mutable more than once at a time
--> src/main.rs:33:22
|
28 | let mut ui = UI::new(&mut stdout)?;
| ----------- first mutable borrow occurs here
...
33 | ui = UI::new(&mut stdout)?;
| -- ^^^^^^^^^^^ second mutable borrow occurs here
| |
| first borrow might be used here, when `ui` is dropped and runs the destructor for type `UI<'_>`
Someone on Reddit suggested to implement take(self) -> &'s mut Stdout
for UI
and it works, but I have no idea why. Playground.
Box
implementsDrop
, so that it can deallocate its contents when destroyed; and thereforeUI
automatically implementsDrop
, which means there is code executed which could accessstdout
between reborrowing it for a newUI
and assigning it toui
. – kmdrekoui
isn't used and therefore doesn't actually do the assignment, but that's just a guess. – kmdrekotake()
is a nice solution IMHO, but note that a more idiomatic name for the method would beinto_stdout()
- in Rust theinto_<something>
methods consumeself
and return the ownership of the thing inside that the object used to own. In this case you get back the original&mut stdout
(with the original lifetime), which allows you to create a newui
that is "compatible" with the original one. – user4815162342