#dynamically-sized #dst #dynamic #dyn #pointers #slice #unsized

nightly dyn_struct2

安全地构建动态大小类型。支持任意非固定大小类型,而不仅仅是切片

1个不稳定版本

0.1.0 2022年8月11日

#1948 in Rust模式


swifer中使用

MIT/Apache

16KB
162

Crates.io docs.rs

dyn_struct2

dyn_structGitHubcrates.io)的修改,支持任何非固定大小类型,而不仅仅是切片。

该包允许您仅使用安全的Rust来初始化动态大小类型(DST)。它还提供帮助,以在非安全Rust中创建DST,这处理了对齐和胖指针的“粗糙”细节。

// Helpers for gritty details
use dyn_struct2::{DynStruct, dyn_arg};
// Automatic #[derive] proc macro
use dyn_struct_derive2::{DynStruct as DynStructDerive};

#[repr(C)]
#[derive(DynStructDerive)]
struct MyDynamicType {
    pub awesome: bool,
    pub number: u32,
    pub dynamic: [u32],
}

fn example() {
    let data = [4, 5, 6, 7];

    // the `new` function is generated by the `DynStruct` macro.
    let foo: Box<MyDynamicType> = MyDynamicType::new(true, 123, dyn_arg!(data));
    assert_eq!(foo.awesome, true);
    assert_eq!(foo.number, 123);
    assert_eq!(&foo.dynamic, &[4, 5, 6, 7]);
    
    // You can also create values unsafely to generate your own constructors, or if you don't like proc macros
    let data2 = [7, 8, 9];
    let foo2 = DynStruct::<(bool, u32), [u32]>::new((false, 456), dyn_arg!(data2));
    let foo2 = unsafe { foo2.transmute::<MyDynamicType>() };
    assert_eq!(foo2.awesome, false);
    assert_eq!(foo2.number, 456);
    assert_eq!(&foo2.dynamic, &[7, 8, 9]);
}

为什么需要动态类型?

在Rust中,动态大小类型(DST)无处不在。切片([T])和特质对象(dyn Trait)是最常见的。然而,您也可以定义自己的类型!例如,这可以通过让结构体中的最后一个字段是DST来实现。

struct MyDynamicType {
    awesome: bool,
    number: u32,
    dynamic: [u32],
}

这告诉Rust编译器,DST的内容在内存中紧接在其他字段之后布局,指向MyDynamicType的指针将是一个包含与DST相同元数据的胖指针。在某些情况下,这可能非常有用,因为它消除了间接层并提高了缓存局部性。

但是,有一个陷阱!就像切片一样,编译器并不知道dynamic的大小。因此,我们需要所谓的胖指针,它存储实际数据的指针以及DST本身的大小(以及附加的元数据,例如,如果是dyn,则vtable)。Rust对DST的原生支持不足,没有安全构建这些DST的方法,因为您实际上不能将非固定类型传递给构造函数或函数参数。此包在后台使用一些unsafe来绕过语言的限制,所有这些都被安全接口所包装。

Derive宏

可以将宏 DynStruct 应用到任何包含动态大小数组作为最后一个字段的 #[repr(C)] struct。

示例

#![cfg(feature = "derive")]
use dyn_struct2::DynStruct;

#[repr(C)]
#[derive(DynStruct)]
struct MyDynamicType {
    pub awesome: bool,
    pub number: u32,
    pub dynamic: [u32],
}

将生成一个包含一个 impl 块和一个 new 函数的单个块。此函数接受与声明顺序相同的所有字段。但是,最后一个字段必须是 DynArg

# use dyn_struct2::DynArg;
#
# #[repr(C)]
# struct MyDynamicType {
#     pub awesome: bool,
#     pub number: u32,
#     pub dynamic: [u32],
# }

impl MyDynamicType {
    pub fn new(awesome: bool, number: u32, dynamic: DynArg<[u32]>) -> Box<MyDynamicType> {
        // ... implementation details ...
        todo!()
    }
}

由于动态大小类型的特性,结果值必须堆上构建。出于安全考虑,我们目前只允许返回 Box,尽管在未来版本中我们可能还会允许 RcArc。在此同时,可以使用 Arc::from(MyDynamicType::new(...))

DynStruct 数据类型

DynStruct 是一种泛型类型,可以采用任何具有 #[repr(C)] 的 DST 的形状。它由一个头部(可能是一个值或静态大小字段的元组)和一个尾部(可能是 DST)组成,实际上是

#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DynStruct<Header, Tail: ?Sized> {
    pub header: Header,
    pub tail: Tail
}

DynStruct 中的关键方法是 DynStruct::transmute,它允许您将此结构体转换为任何动态大小的结构。这是创建您自己的 DST 的安全构造函数的简单但不可安全的方式

# use dyn_struct2::{DynStruct, DynArg};
#
# #[repr(C)]
# struct MyDynamicType {
#     pub awesome: bool,
#     pub number: u32,
#     pub dynamic: [u32],
# }

impl MyDynamicType {
    pub fn new(awesome: bool, number: u32, dynamic: DynArg<[u32]>) -> Box<MyDynamicType> {
        // This is the actual generated body of MyDynamicType if you use #[derive(DynStruct)]!
        let dyn_struct = DynStruct::new((awesome, number), dynamic);
        unsafe { dyn_struct.transmute() }
    }
}

DynArg

DynArg 是动态大小类型的包装器,以便您可以消费它们并将它们作为参数传递给函数。您可以使用宏 dyn_arg!(arg) 创建一个 DynArg

调用 dyn_arg!(arg) 后,arg 实际上被消耗。但是,除非您将 dyn_arg 传递给 DynStruct 构造函数然后丢弃构建的值,否则它不会被丢弃。如果您只是忽略 dyn_arg,则 arg 将泄漏,这在技术上是一种安全的行为,但请注意。

依赖项

~220KB