我正在尝试为泛型类型动态分配内存,并希望将泛型类型包装在 Arc 中。但是,我遇到了分段错误。
当我将T
in包装起来时,代码运行良好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)
问题
Box
有效而Arc
无效?Arc
在使用分配完成时有效,Vec::with_capacity()
但在alloc()
使用时无效?游乐场链接
圆弧使用alloc
(对于第一种情况,请将圆弧更改为方框)。
这条线是不健全的:
let raw = unsafe { from_raw_parts_mut(mem as *mut Cell<Entry<T>>, num) };
Run Code Online (Sandbox Code Playgroud)
创建对num
元素切片的引用是不正确的,因为已经初始化了 0 个元素。当T
isArc<u8>
是指针类型时,这意味着它们可能指向任意数据,或者为空。如果您只创建了切片引用并且从未取消引用它,它可能会起作用,但是这一行:
self.vec[self.index].set(Entry { val: val.clone() });
Run Code Online (Sandbox Code Playgroud)
会尝试将下降Arc
已经在Cell
与新的替换它之前Arc
。删除Arc
指针会释放指针,因为它未初始化会导致未定义的行为。
为什么对动态分配的内存
Box
有效而Arc
无效?
偶然,基本上。Drop
forBox
只是将指针传递给free
,但Drop
forArc
必须跟随它以检查引用计数。如果内存最初归零,使用典型的分配器,Box
可能会在出现段错误时工作Arc
。但是,您不能依赖这种行为,这使我想到了下一点:
当我将
T
in包装起来时,代码运行良好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
已经存在?)