使用可变引用覆盖变量时借用检查器错误
Borrow checker error when overwriting variable with mutable reference
我有一个结构 UI
持有对 Stdout
的可变引用。与其在更新时改变它,我更愿意用一个全新的 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(())
}
借用检查器抱怨 stdout 被可变地借用了两次:
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<'_>`
有两个奇怪的行为:
- 如果我删除字段
v
,错误就会消失。
- 如果我删除
ui.display()?
,错误也会消失。
这里有什么问题?
The first point is explained in the error message, albeit in a roundabout way if you don't know what's going on. Box
implements Drop
, so that it can deallocate its contents when destroyed; and therefore UI
automatically implements Drop
, which means there is code executed which could access stdout
between reborrowing it for a new UI
and assigning it to ui
.
那为什么这个 return 出错了?
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.
替换值时,必须先创建新值:
struct Noisy(u8);
impl Noisy {
fn new(v: u8) -> Self {
eprintln!("creating {}", v);
Self(v)
}
}
impl Drop for Noisy {
fn drop(&mut self) {
eprintln!("dropping {}", self.0);
}
}
fn main() {
let mut ui = Noisy::new(1);
ui = Noisy::new(2);
}
creating 1
creating 2
dropping 1
dropping 2
这意味着您的两个 UI
结构将尝试共存。由于它们都有对 stdout
的可变引用,Drop::drop
实现 可能 发生变异 stdout
,这将违反引用规则一次有多个活动的可变引用。
当您不调用 write
时,非词法借用检查器认为不需要借用,所以没有问题。
Then why does [explicitly dropping the value before reassigning it] return an error?
因为那是a limitation of the current borrow checker implementation.
另请参阅:
- What are non-lexical lifetimes?
- Moved variable still borrowing after calling `drop`?
我有一个结构 UI
持有对 Stdout
的可变引用。与其在更新时改变它,我更愿意用一个全新的 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(())
}
借用检查器抱怨 stdout 被可变地借用了两次:
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<'_>`
有两个奇怪的行为:
- 如果我删除字段
v
,错误就会消失。 - 如果我删除
ui.display()?
,错误也会消失。
这里有什么问题?
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
.
那为什么这个 return 出错了?
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.
替换值时,必须先创建新值:
struct Noisy(u8);
impl Noisy {
fn new(v: u8) -> Self {
eprintln!("creating {}", v);
Self(v)
}
}
impl Drop for Noisy {
fn drop(&mut self) {
eprintln!("dropping {}", self.0);
}
}
fn main() {
let mut ui = Noisy::new(1);
ui = Noisy::new(2);
}
creating 1
creating 2
dropping 1
dropping 2
这意味着您的两个 UI
结构将尝试共存。由于它们都有对 stdout
的可变引用,Drop::drop
实现 可能 发生变异 stdout
,这将违反引用规则一次有多个活动的可变引用。
当您不调用 write
时,非词法借用检查器认为不需要借用,所以没有问题。
Then why does [explicitly dropping the value before reassigning it] return an error?
因为那是a limitation of the current borrow checker implementation.
另请参阅:
- What are non-lexical lifetimes?
- Moved variable still borrowing after calling `drop`?