如何使用嵌入切片创建指向未调整大小类型的智能指针?
How to create a smart pointer to an unsized type with an embedded slice?
我正在尝试通过使用类似于 C 的灵活数组成员之类的东西来避免多次堆分配。为此,我需要分配一个未确定大小的结构,但我没有找到任何方法通过智能指针来做到这一点。我对 Rc
特别感兴趣,但 Box
也是如此,所以我将在示例中使用它。
这是我目前最接近的:
use std::alloc::{self, Layout};
struct Inner {/* Sized fields */}
#[repr(C)] // Ensure the array is always last
// Both `inner` and `arr` need to be allocated, but preferably not separately
struct Unsized {
inner: Inner,
arr: [usize],
}
pub struct Exposed(Box<Unsized>);
impl Exposed {
pub fn new(capacity: usize) -> Self {
// Create a layout of an `Inner` followed by the array
let (layout, arr_base) = Layout::array::<usize>(capacity)
.and_then(|arr_layout| Layout::new::<Inner>().extend(arr_layout))
.unwrap();
let ptr = unsafe { alloc::alloc(layout) };
// At this point, `ptr` is `*mut u8` and the compiler doesn't know the size of the allocation
if ptr.is_null() {
panic!("Internal allocation error");
}
unsafe {
ptr.cast::<Inner>()
.write(Inner {/* Initialize sized fields */});
let tmp_ptr = ptr.add(arr_base).cast::<usize>();
// Initialize the array elements, in this case to 0
(0..capacity).for_each(|i| tmp_ptr.add(i).write(0));
// At this point everything is initialized and can safely be converted to `Box`
Self(Box::from_raw(ptr as *mut _))
}
}
}
这不编译:
error[E0607]: cannot cast thin pointer `*mut u8` to fat pointer `*mut Unsized`
--> src/lib.rs:32:28
|
32 | Self(Box::from_raw(ptr as *mut _))
| ^^^^^^^^^^^^^
我可以直接使用 *mut u8
,但这似乎非常容易出错,需要手动删除。
有没有一种方法可以从 ptr
创建一个胖指针,因为我实际上知道分配大小,或者可以从复合未确定大小的类型创建一个智能指针?
问题是指针*mut Unsized
是一个宽指针,所以不仅仅是一个地址,而是一个地址和切片中元素的数量。另一方面,指针 *mut u8
不包含有关切片长度的信息。标准库提供
std::ptr::slice_from_raw_parts
并且,
std::ptr::slice_from_raw_parts_mut
对于这种情况。所以你首先创建一个假的(错误的)*mut usize
ptr as *mut usize
然后允许
slice_from_raw_parts_mut(ptr as *mut usize, capacity)
创建一个伪造的(仍然是错误的)*mut [usize]
,在宽指针中使用正确的长度字段,然后我们毫不客气地对其进行转换
slice_from_raw_parts_mut(ptr as *mut usize, capacity) as *mut Unsized
除了改变类型什么都不做(值不变),所以我们得到了正确的指针,我们现在可以最终将其输入 Box::from_raw
完整示例演示 post:
use std::alloc::{self, Layout};
struct Inner {/* Sized fields */}
#[repr(C)] // Ensure the array is always last
// Both `inner` and `arr` need to be allocated, but preferably not separately
struct Unsized {
inner: Inner,
arr: [usize],
}
pub struct Exposed(Box<Unsized>);
impl Exposed {
pub fn new(capacity: usize) -> Self {
// Create a layout of an `Inner` followed by the array
let (layout, arr_base) = Layout::array::<usize>(capacity)
.and_then(|arr_layout| Layout::new::<Inner>().extend(arr_layout))
.unwrap();
let ptr = unsafe { alloc::alloc(layout) };
// At this point, `ptr` is `*mut u8` and the compiler doesn't know the size of the allocation
if ptr.is_null() {
panic!("Internal allocation error");
}
unsafe {
ptr.cast::<Inner>()
.write(Inner {/* Initialize sized fields */});
let tmp_ptr = ptr.add(arr_base).cast::<usize>();
// Initialize the array elements, in this case to 0
(0..capacity).for_each(|i| tmp_ptr.add(i).write(0));
}
// At this point everything is initialized and can safely be converted to `Box`
unsafe {
Self(Box::from_raw(
std::ptr::slice_from_raw_parts_mut(ptr as *mut usize, capacity) as *mut Unsized,
))
}
}
}
旁注:您不需要 #[repr(C)]
来确保未调整大小的切片字段位于末尾,这是有保证的。您需要它的目的是了解字段的偏移量。
我正在尝试通过使用类似于 C 的灵活数组成员之类的东西来避免多次堆分配。为此,我需要分配一个未确定大小的结构,但我没有找到任何方法通过智能指针来做到这一点。我对 Rc
特别感兴趣,但 Box
也是如此,所以我将在示例中使用它。
这是我目前最接近的:
use std::alloc::{self, Layout};
struct Inner {/* Sized fields */}
#[repr(C)] // Ensure the array is always last
// Both `inner` and `arr` need to be allocated, but preferably not separately
struct Unsized {
inner: Inner,
arr: [usize],
}
pub struct Exposed(Box<Unsized>);
impl Exposed {
pub fn new(capacity: usize) -> Self {
// Create a layout of an `Inner` followed by the array
let (layout, arr_base) = Layout::array::<usize>(capacity)
.and_then(|arr_layout| Layout::new::<Inner>().extend(arr_layout))
.unwrap();
let ptr = unsafe { alloc::alloc(layout) };
// At this point, `ptr` is `*mut u8` and the compiler doesn't know the size of the allocation
if ptr.is_null() {
panic!("Internal allocation error");
}
unsafe {
ptr.cast::<Inner>()
.write(Inner {/* Initialize sized fields */});
let tmp_ptr = ptr.add(arr_base).cast::<usize>();
// Initialize the array elements, in this case to 0
(0..capacity).for_each(|i| tmp_ptr.add(i).write(0));
// At this point everything is initialized and can safely be converted to `Box`
Self(Box::from_raw(ptr as *mut _))
}
}
}
这不编译:
error[E0607]: cannot cast thin pointer `*mut u8` to fat pointer `*mut Unsized`
--> src/lib.rs:32:28
|
32 | Self(Box::from_raw(ptr as *mut _))
| ^^^^^^^^^^^^^
我可以直接使用 *mut u8
,但这似乎非常容易出错,需要手动删除。
有没有一种方法可以从 ptr
创建一个胖指针,因为我实际上知道分配大小,或者可以从复合未确定大小的类型创建一个智能指针?
问题是指针*mut Unsized
是一个宽指针,所以不仅仅是一个地址,而是一个地址和切片中元素的数量。另一方面,指针 *mut u8
不包含有关切片长度的信息。标准库提供
std::ptr::slice_from_raw_parts
并且,std::ptr::slice_from_raw_parts_mut
对于这种情况。所以你首先创建一个假的(错误的)*mut usize
ptr as *mut usize
然后允许
slice_from_raw_parts_mut(ptr as *mut usize, capacity)
创建一个伪造的(仍然是错误的)*mut [usize]
,在宽指针中使用正确的长度字段,然后我们毫不客气地对其进行转换
slice_from_raw_parts_mut(ptr as *mut usize, capacity) as *mut Unsized
除了改变类型什么都不做(值不变),所以我们得到了正确的指针,我们现在可以最终将其输入 Box::from_raw
完整示例演示 post:
use std::alloc::{self, Layout};
struct Inner {/* Sized fields */}
#[repr(C)] // Ensure the array is always last
// Both `inner` and `arr` need to be allocated, but preferably not separately
struct Unsized {
inner: Inner,
arr: [usize],
}
pub struct Exposed(Box<Unsized>);
impl Exposed {
pub fn new(capacity: usize) -> Self {
// Create a layout of an `Inner` followed by the array
let (layout, arr_base) = Layout::array::<usize>(capacity)
.and_then(|arr_layout| Layout::new::<Inner>().extend(arr_layout))
.unwrap();
let ptr = unsafe { alloc::alloc(layout) };
// At this point, `ptr` is `*mut u8` and the compiler doesn't know the size of the allocation
if ptr.is_null() {
panic!("Internal allocation error");
}
unsafe {
ptr.cast::<Inner>()
.write(Inner {/* Initialize sized fields */});
let tmp_ptr = ptr.add(arr_base).cast::<usize>();
// Initialize the array elements, in this case to 0
(0..capacity).for_each(|i| tmp_ptr.add(i).write(0));
}
// At this point everything is initialized and can safely be converted to `Box`
unsafe {
Self(Box::from_raw(
std::ptr::slice_from_raw_parts_mut(ptr as *mut usize, capacity) as *mut Unsized,
))
}
}
}
旁注:您不需要 #[repr(C)]
来确保未调整大小的切片字段位于末尾,这是有保证的。您需要它的目的是了解字段的偏移量。