gpt4 book ai didi

templates - 在 Arc 中包装泛型类型的堆分配内存?

转载 作者:行者123 更新时间:2023-12-03 23:19:21 25 4
gpt4 key购买 nike

我正在尝试为泛型类型动态分配内存,并希望将泛型类型包装在 Arc 中。但是,我遇到了段错误。

当我包装 T 时,代码工作正常在Box ,这意味着从语义上讲,代码没有问题。

此外,当我使用 Vec::with_capacity() 时,代码工作正常,而不是使用 alloc() 动态分配内存.

struct Entry<T>
where
T: Sized + Clone + Default,
{
val: T,
}

struct Entries<'a, T>
where
T: Sized + Clone + Default,
{
vec: &'a [Cell<Entry<T>>],
index: usize,
}

impl<'a, T> Entries<'a, T>
where
T: Sized + Clone + Default,
{
fn new<'b>() -> Entries<'b, T> {
let bytes = 64;
let mem = unsafe {
alloc(Layout::from_size_align(bytes, align_of::<Cell<Entry<T>>>()).expect("Error"))
};

let num = bytes / size_of::<Cell<Entry<T>>>();
let raw = unsafe { from_raw_parts_mut(mem as *mut Cell<Entry<T>>, num) };

Entries { vec: raw, index: 0 }
}

fn append(&mut self, val: &T) {
self.vec[self.index].set(Entry { val: val.clone() }); // Error here
self.index += 1;
println!("Appended");
}

fn remove(&mut self) {
self.index -= 1;
println!("Removed");
}
}

fn main() {
let mut log = Entries::<Arc<u8>>::new();
let element = Arc::new(8);
log.append(&element);
println!("Reference Count for Arc {}", Arc::strong_count(&element));
log.remove();
println!("Reference Count for Arc {}", Arc::strong_count(&element));
}

问题

  1. 为什么是Box工作和Arc不适用于动态分配的内存?
  2. 为什么是Arc使用 Vec::with_capacity() 完成分配后工作但不是在alloc()时使用过吗?

Playground 链接

  1. Arc using alloc (第一种情况请将Arc改为Box)。

  2. Arc using Vec::with_capacity

最佳答案

这行不健全:

let raw = unsafe { from_raw_parts_mut(mem as *mut Cell<Entry<T>>, num) };

创建对 num 元素切片的引用是不正确的,因为已初始化 0 个元素。当 TArc<u8> (指针类型)时,这意味着它们可以指向任意数据,或者为空。如果您只创建了切片引用并且从未取消引用它,它可能会起作用,但是这一行:

self.vec[self.index].set(Entry { val: val.clone() });

将尝试删除 Arc 中已有的 Cell ,然后将其替换为新的 Arc 。删除 Arc 会释放指针,因为它未初始化,因此会导致未定义的行为。

Why is Box working and Arc not working for dynamically allocated memory?

基本上是偶然的。 DropBox 只是将指针传递给 free ,但 DropArc 必须遵循它才能检查引用计数。如果内存最初被清零,使用典型的分配器,Box 可能看起来可以工作,而 Arc 会出现段错误。但是,您不能依赖这种行为,这引出了我的下一点:

The code is working fine when I wrap the T in Box, which means that semantically there is no issue with the code.

这不是真的。因为代码看起来可以正常工作并不意味着它没有错误!甚至 Box 版本也不正确。避免内存安全错误的最佳方法是不使用 unsafe 。当您使用 unsafe 时,您必须小心维护安全代码所依赖的所有不变量,例如(在本例中)“引用必须始终有效”。

Why is Arc working when the allocation is done using Vec::with_capacity() but not when alloc() is used?

Vec::with_capacity 创建一个 Vec 。当您在此空 Vec::push 上调用 Vec 时,它​​不会在索引 0 处的未初始化元素上调用 drop ;它只是初始化它(并将长度增加到 1)。

这是一个很好的例子,说明了为什么正确使用 unsafe 比看起来更棘手,以及为什么大多数 Rust 程序员更喜欢使用经过彻底测试和审查的数据结构,例如 Vec ,而不是使用 unsafe 自己创建。

( Entries 至少还有一个问题,那就是它引用的数据可能永远不会被正确释放;因为 new 创建一个具有任意生命周期的 Entries,您可以使用它来获取对内部 block 的引用,该内部 block 的生命周期比Entries 对象本身。alloc 创建的数据无法被释放,因此是无主的。解决这个问题归结为使用更多的 unsafe ,并在 Vec 中重新创建大部分 Entries ——但是为什么要麻烦呢,因为 Vec 已经存在了?)

关于templates - 在 Arc 中包装泛型类型的堆分配内存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59572550/

25 4 0