rust - 如何使用嵌入切片创建指向未调整大小类型的智能指针?

标签 rust smart-pointers

我试图通过使用像 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展示这篇文章的完整示例:
    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,
                ))
            }
        }
    }
    
    Playground

    旁注:您不需要 #[repr(C)]确保未调整大小的切片字段在最后,这是有保证的。您需要它来了解字段的偏移量。

    关于rust - 如何使用嵌入切片创建指向未调整大小类型的智能指针?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64120001/

    相关文章:

    rust - 产生每个部分结果的折叠算法

    rust - mem::forget(mem::uninitialized()) 是否定义了行为?

    c++ - 在 Boost::bimap 中使用指针

    arrays - 在 Cython 中使用智能指针动态分配数组

    c++ - 如何仅强制类的智能指针实例?

    C++:创建共享对象而不是指向对象的共享指针

    rust - 将 Vec<Vec<u8>> 转换为 &[&[u8]]

    rust - 如何在不克隆的情况下对 Vec<(Cow<str>, Cow<str>) by_key 进行排序?

    rust - 为什么 Rust 需要知道模块中的代码属于谁?

    C++ 交换 unique_ptr's