为什么生命周期对 slice::from_raw_parts 很重要?
Why is the lifetime important for slice::from_raw_parts?
slice::from_raw_parts
的文档警告程序员用正确的生命周期注释切片。我假设,给定一些生命周期 'a
,我可以使用
执行此注释
let myslice: &'a mut [i32] = std::slice::from_raw_parts_mut(ptr, sz)
我也假设
- 由于
myslice
是引用,与ptr指向的底层数据allocation/deallocation没有任何关系。生命周期注释不影响数据的内存管理。
-
myslice
本身的内存管理没有什么棘手的(即包含指针和大小的结构)。它就像任何其他结构或 i32
。如果我把它放在 Box
中,那么 std::raw::slice
结构将在 Box
死亡时被释放。当然,切片引用的数据不会被释放。生命周期不影响切片的内存管理。
为什么我得到正确的生命周期很重要?设置切片生命周期时,释放后使用是唯一要担心的危险吗?
更新: 正如 delnan 所说,可变别名是一个真正的问题,可能由设置不正确的生命周期引起。有关详细信息,请参阅 his/her 答案。
旧答案
释放后使用确实是设置切片生命周期时唯一需要担心的危险。编译器将信任您并假定切片指向的数据与您指定的生命周期一样长。如果您的注释生命周期长于底层数据的实际生命周期,您最终可能会遇到释放后使用错误(您将能够在数据已被释放时使用切片)。
关于你的假设,它们是正确的。生命周期注释对数据的内存管理没有任何影响。
释放后使用不是唯一的危险。错误的生命周期会导致可变别名。让我们以这个(人为的)函数为例:
fn duplicate_mut_slice<'a, T>(xs: &mut [T]) -> &'a mut [T] {
let ptr = xs.as_mut_ptr(); // btw, this part is safe!
unsafe { std::slice::from_raw_parts_mut(ptr, xs.len()) }
}
由于生命周期的排列方式,这样的调用将会成功:
fn alias_first_element<T>(xs: &mut [T]) -> (&mut T, &mut T) {
let a = duplicate_mut_slice(xs);
let b = duplicate_mut_slice(xs);
(&mut a[0], &mut b[0])
}
请注意,在第二个函数的签名中,生命周期是正确的,释放后使用不是(直接)危险。但是可变别名非常隐蔽。基本上一切都依赖于保证不存在可变别名,以防止竞争条件、迭代器失效、逻辑错误和释放后使用(由 管理的 T
等问题) ).可变别名几乎可以引起任何可以想象到的问题。
slice::from_raw_parts
的文档警告程序员用正确的生命周期注释切片。我假设,给定一些生命周期 'a
,我可以使用
let myslice: &'a mut [i32] = std::slice::from_raw_parts_mut(ptr, sz)
我也假设
- 由于
myslice
是引用,与ptr指向的底层数据allocation/deallocation没有任何关系。生命周期注释不影响数据的内存管理。 -
myslice
本身的内存管理没有什么棘手的(即包含指针和大小的结构)。它就像任何其他结构或i32
。如果我把它放在Box
中,那么std::raw::slice
结构将在Box
死亡时被释放。当然,切片引用的数据不会被释放。生命周期不影响切片的内存管理。
为什么我得到正确的生命周期很重要?设置切片生命周期时,释放后使用是唯一要担心的危险吗?
更新: 正如 delnan 所说,可变别名是一个真正的问题,可能由设置不正确的生命周期引起。有关详细信息,请参阅 his/her 答案。
旧答案
释放后使用确实是设置切片生命周期时唯一需要担心的危险。编译器将信任您并假定切片指向的数据与您指定的生命周期一样长。如果您的注释生命周期长于底层数据的实际生命周期,您最终可能会遇到释放后使用错误(您将能够在数据已被释放时使用切片)。
关于你的假设,它们是正确的。生命周期注释对数据的内存管理没有任何影响。
释放后使用不是唯一的危险。错误的生命周期会导致可变别名。让我们以这个(人为的)函数为例:
fn duplicate_mut_slice<'a, T>(xs: &mut [T]) -> &'a mut [T] {
let ptr = xs.as_mut_ptr(); // btw, this part is safe!
unsafe { std::slice::from_raw_parts_mut(ptr, xs.len()) }
}
由于生命周期的排列方式,这样的调用将会成功:
fn alias_first_element<T>(xs: &mut [T]) -> (&mut T, &mut T) {
let a = duplicate_mut_slice(xs);
let b = duplicate_mut_slice(xs);
(&mut a[0], &mut b[0])
}
请注意,在第二个函数的签名中,生命周期是正确的,释放后使用不是(直接)危险。但是可变别名非常隐蔽。基本上一切都依赖于保证不存在可变别名,以防止竞争条件、迭代器失效、逻辑错误和释放后使用(由 管理的 T
等问题) ).可变别名几乎可以引起任何可以想象到的问题。