admin管理员组文章数量:1345391
I ran into a surprising (to me) error while using RefCell and I want to understand better why this is happening. I had something like this code below where I have a while let block consuming a mutable function on a borrowed RefCell:
struct Foo {
val: i32,
}
impl Foo {
fn increment(&mut self) -> Option<i32> {
if self.val >= 10 {
return None;
}
self.val += 1;
Some(self.val)
}
}
fn main() {
let r = RefCell::new(Foo { val: 0 });
while let Some(v) = r.borrow_mut().increment() {
println!("iteration: {}", v);
println!("borrow: {}", r.borrow().val); // panic!: BorrowError, already mutably borrowed
}
}
But clearly I don't use the r mutable reference after it's created, so why is it still alive? The way I found to fix this is:
while let Some(v) = {
let mut borrowed = r.borrow_mut();
borrowed.increment()
} {
println!("iteration: {}", v);
println!("borrow: {}", r.borrow().val); // now this works fine
}
But then if I try removing the temporary, even within the scope, it still breaks.
while let Some(v) = { r.borrow_mut().increment() } {
println!("iteration: {}", v);
println!("borrow: {}", r.borrow().val); // panic! BorrowError
}
Also, these errors seem to me to be specific to while let
, because I didnt run into this error when just checking some property directly with no while let.
So what exactly is the mechanism/rule here that governs how RefCell updates its borrow counter?
I ran into a surprising (to me) error while using RefCell and I want to understand better why this is happening. I had something like this code below where I have a while let block consuming a mutable function on a borrowed RefCell:
struct Foo {
val: i32,
}
impl Foo {
fn increment(&mut self) -> Option<i32> {
if self.val >= 10 {
return None;
}
self.val += 1;
Some(self.val)
}
}
fn main() {
let r = RefCell::new(Foo { val: 0 });
while let Some(v) = r.borrow_mut().increment() {
println!("iteration: {}", v);
println!("borrow: {}", r.borrow().val); // panic!: BorrowError, already mutably borrowed
}
}
But clearly I don't use the r mutable reference after it's created, so why is it still alive? The way I found to fix this is:
while let Some(v) = {
let mut borrowed = r.borrow_mut();
borrowed.increment()
} {
println!("iteration: {}", v);
println!("borrow: {}", r.borrow().val); // now this works fine
}
But then if I try removing the temporary, even within the scope, it still breaks.
while let Some(v) = { r.borrow_mut().increment() } {
println!("iteration: {}", v);
println!("borrow: {}", r.borrow().val); // panic! BorrowError
}
Also, these errors seem to me to be specific to while let
, because I didnt run into this error when just checking some property directly with no while let.
So what exactly is the mechanism/rule here that governs how RefCell updates its borrow counter?
- 1 The last example will work correctly when using the Rust 2024 edition - the scopes of temporaries within blocks were changed to not "leak out" to the surrounding temporary scope as they did before. Documentation. – kmdreko Commented 20 hours ago
1 Answer
Reset to default 4But clearly I don't use the r mutable reference after it's created, so why is it still alive?
RefCell::borrow_mut()
does not just return a mutable reference, but a RefMut
. This is a struct with a destructor that updates the RefCell
borrow count, so it is subject to the rules for when destructors run. Those rules specify the timing purely in terms of scopes — not “after the last use” like borrow checking allows for a plain &mut Foo
.
In the case of while let Some(v) = r.borrow_mut().increment()
, the RefMut
is not assigned to any named variable, which means it is instead stored in a temporary variable which is dropped at the end of the temporary scope. Unfortunately, that page seems to have fotten to mention while let
(fix?), but the behavior is the same as for if let
; the temporary scope for the condition of a while let
is the entire body. This is intentionally designed so that you can use borrows from the temporaries in a if let
or while let
. For example, this code can compile:
if let Some(counter) = &mut some_cell.borrow_mut().optional_field {
counter += 1;
But if temporary scope was narrower, then the RefMut
would be dropped too soon and borrowing would be impossible.
本文标签:
版权声明:本文标题:rust - Why does a refcell borrowed within an expression live longer than intended (while borrowing to a variable works) - Stack 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1743781352a2537887.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论