在 Arc 中为泛型类型包装堆分配内存?

A-B*_*A-B 2 templates rust

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

当我将Tin包装起来时,代码运行良好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));
}
Run Code Online (Sandbox Code Playgroud)

问题

  1. 为什么对动态分配的内存Box有效而Arc无效?
  2. 为什么Arc在使用分配完成时有效,Vec::with_capacity()但在alloc()使用时无效?

游乐场链接

  1. 圆弧使用alloc(对于第一种情况,请将圆弧更改为方框)。

  2. 电弧使用 Vec::with_capacity

tre*_*tcl 5

这条线是不健全的:

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

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

self.vec[self.index].set(Entry { val: val.clone() });
Run Code Online (Sandbox Code Playgroud)

会尝试将下降Arc已经在Cell与新的替换它之前Arc。删除Arc指针会释放指针,因为它未初始化会导致未定义的行为。

为什么对动态分配的内存Box有效而Arc无效?

偶然,基本上。DropforBox只是将指针传递给free,但DropforArc必须跟随它以检查引用计数。如果内存最初归零,使用典型的分配器,Box可能会在出现段错误时工作Arc。但是,您不能依赖这种行为,这使我想到了下一点:

当我将Tin包装起来时,代码运行良好Box,这意味着代码在语义上没有问题。

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

为什么Arc在使用分配完成时有效,Vec::with_capacity()但在alloc()使用时无效?

Vec::with_capacity创建一个空的 Vec. 当你调用Vec::push这个空白Vec,它并没有调用drop索引0未初始化的元素; 它只是初始化它(并将长度增加到 1)。

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

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