gpt4 book ai didi

Why can't I use a reference to a `Bump` after a call to `reset`?(为什么不能在调用`Reset`之后使用对`Bump`的引用?)

转载 作者:bug小助手 更新时间:2023-10-26 20:36:34 28 4
gpt4 key购买 nike



I'm currently testing out arenas. I actually expected this code to compile but fail at runtime, so pleasantly surprised the compiler caught the issue. But I don't know if it's reasoning is correct. Can someone explain it to me?

我目前正在测试竞技场。我本来以为这段代码会编译,但在运行时失败了,令人惊喜的是,编译器发现了这个问题。但我不知道它的推理是否正确。有人能给我解释一下吗?


use bumpalo::Bump;

fn main() {
let mut bump = Bump::new();
let b1: &mut Bob = bump.alloc(Bob::default());
let b2: &mut Bob = bump.alloc(Bob::default());
let b3: &mut Bob = bump.alloc(Bob::new(Some(b1), Some(b2)));
println!("{:?}", b2);
{
bump.reset();
}
println!("{:?}", b2);
}

#[derive(Debug)]
struct Bob {
son: Option<*mut Bob>,
parent: Option<*mut Bob>,
string: String,
}
impl Bob {
fn new(son: Option<*mut Bob>, parent: Option<*mut Bob>) -> Bob {
Bob {
son,
parent,
..Bob::default()
}
}
}

impl Default for Bob {
fn default() -> Bob {
Bob {
son: None,
parent: None,
string: String::from("Hello"),
}
}
}

Error:

错误:


error[E0502]: cannot borrow `bump` as mutable because it is also borrowed as immutable
--> src\main.rs:10:9
|
6 | let b2: &mut Bob = bump.alloc(Bob::default());
| -------------------------- immutable borrow occurs here
...
10 | bump.reset();
| ^^^^^^^^^^^^ mutable borrow occurs here
11 | }
12 | println!("{:?}", b2);
| -- immutable borrow later used here

Relevant code from bumpalo:

来自umpalo的相关代码:


pub fn alloc<T>(&self, val: T) -> &mut T {
self.alloc_with(|| val)
}

#[inline(always)]
#[allow(clippy::mut_from_ref)]
pub fn alloc_with<F, T>(&self, f: F) -> &mut T
where
F: FnOnce() -> T,
{
#[inline(always)]
unsafe fn inner_writer<T, F>(ptr: *mut T, f: F)
where
F: FnOnce() -> T,
{
// This function is translated as:
// - allocate space for a T on the stack
// - call f() with the return value being put onto this stack space
// - memcpy from the stack to the heap
//
// Ideally we want LLVM to always realize that doing a stack
// allocation is unnecessary and optimize the code so it writes
// directly into the heap instead. It seems we get it to realize
// this most consistently if we put this critical line into it's
// own function instead of inlining it into the surrounding code.
ptr::write(ptr, f())
}

let layout = Layout::new::<T>();

unsafe {
let p = self.alloc_layout(layout);
let p = p.as_ptr() as *mut T;
inner_writer(p, f);
&mut *p
}
}

pub fn reset(&mut self) {
// Takes `&mut self` so `self` must be unique and there can't be any
// borrows active that would get invalidated by resetting.
unsafe {
if self.current_chunk_footer.get().as_ref().is_empty() {
return;
}

let mut cur_chunk = self.current_chunk_footer.get();

// Deallocate all chunks except the current one
let prev_chunk = cur_chunk.as_ref().prev.replace(EMPTY_CHUNK.get());
dealloc_chunk_list(prev_chunk);

// Reset the bump finger to the end of the chunk.
cur_chunk.as_ref().ptr.set(cur_chunk.cast());

// Reset the allocated size of the chunk.
cur_chunk.as_mut().allocated_bytes = cur_chunk.as_ref().layout.size();

debug_assert!(
self.current_chunk_footer
.get()
.as_ref()
.prev
.get()
.as_ref()
.is_empty(),
"We should only have a single chunk"
);
debug_assert_eq!(
self.current_chunk_footer.get().as_ref().ptr.get(),
self.current_chunk_footer.get().cast(),
"Our chunk's bump finger should be reset to the start of its allocation"
);
}
}

更多回答

Uh, this is pretty basic. All immutable references to bump, like b1, b2, and b3 must end their lifetimes before you take a mutable reference to bump to call reset. Exactly so that you don't dereference b3 after deallocating it.

呃,这是很基本的。所有对bump的不可变引用,如b1、b2和b3,必须在获取对bump的可变引用以调用Reset之前结束其生存期。准确地说,这样就不会在取消分配后取消对b3的引用。

优秀答案推荐

The signature for Bump.alloc is:

Bump.alloc的签名是:


pub fn alloc<T>(&self, val: T) -> &mut T

This signature is exploiting Lifetime Elision rules to avoid including explicit lifetime information. The relevant rule is:

该签名利用生存期省略规则来避免包含显式生存期信息。相关的规则是:



If there are multiple input lifetime positions, but one of them is &self or &mut self, the lifetime of self is assigned to all elided output lifetimes.



All references in a signature must have some lifetime, so the compiler infers some for this function. Based on the above rule, the signature is effectively:

签名中的所有引用都必须有一定的生存期,因此编译器会为该函数推断出一些生存期。基于上述规则,签名是有效的:


pub fn alloc<T>(&'a self, val: T) -> &'a mut T

...or in other words, the result of this function must not outlive the Bump that self refers to, which is hopefully intuitive because the Bump owns the memory that the allocation belongs to.

...或者换句话说,此函数的结果不能超过Self引用的Bump,这希望是直观的,因为Bump拥有分配所属的内存。


The Bump.reset function has this signature:

Bump.Reset函数具有以下签名:


pub fn reset(&mut self)

This method takes a mut (exclusive) reference to self, which means it cannot be called while any other reference to the same object is "live". "Live" here means that later code might make use of it.

此方法接受对self的mut(独占)引用,这意味着当对同一对象的任何其他引用是“live”时,不能调用它。这里的“Live”意味着以后的代码可能会使用它。


Your code tries to use b2 after calling reset, which means that the b2 reference remains live across the call to reset. b2 has the same lifetime as this &mut self, so that is illegal. The reference b2 is effectively a reference to the Bump object (a part of the memory that belongs to it), and so it shares the same lifetime as described above.

您的代码在调用Reset之后尝试使用b2,这意味着b2引用在调用Reset的整个过程中保持有效。B2与这个具有相同的生命周期&mut self,所以这是非法的。引用b2实际上是对凹凸对象(属于它的内存的一部分)的引用,因此它具有如上所述的相同生存期。


If you remove that final reference to b2 after reset then this code should compile, because then none of the references to parts of bump will still be live at the time of reset.

如果在重置后删除对b2最终引用,则应该编译此代码,因为在重置时,对凹凸部分的引用将不再有效。


更多回答

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com