3 个版本 (破坏性更新)

0.3.2 2022 年 8 月 14 日
0.3.1 2022 年 8 月 14 日
0.3.0 2022 年 8 月 14 日
0.2.0 2021 年 10 月 20 日
0.1.0 2021 年 10 月 19 日

#1627 in Rust 模式

MIT/Apache

16KB
196

Crates.io docs.rs

dyn_struct

该软件包允许您仅使用安全的 Rust 来安全地初始化动态大小类型 (DST)。

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

// the `new` function is generated by the `DynStruct` macro.
let foo: Box<MyDynamicType> = MyDynamicType::new(true, 123, [4, 5, 6, 7]);
assert_eq!(foo.awesome, true);
assert_eq!(foo.number, 123);
assert_eq!(&foo.dynamic, &[4, 5, 6, 7]);

为什么是动态类型?

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

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

这告诉 Rust 编译器,动态数组的内存布局紧接在其他字段之后。在某些情况下,这可能会非常理想,因为它减少了一层间接引用并提高了缓存局部性。

然而,有一个问题!就像切片一样,编译器不知道 dynamic 中有多少元素。因此,我们需要所谓的胖指针,它存储实际数据的指针以及数组的长度。截至发布此软件包时,唯一安全构造动态类型的方法是在编译时知道数组的长度。然而,对于大多数用例,这是不可能的。因此,该软件包在后台使用一些 unsafe 来解决语言的限制,并通过一个安全接口包装起来。

Derive 宏

DynStruct 宏可以应用于任何包含动态大小数组作为最后一个字段的 #[repr(C)] 结构体。字段只有一个约束:它们必须实现 Copy

示例

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

将产生一个包含 new 函数的单个 impl 块。此函数接受与声明顺序相同的所有字段。但是,最后一个字段可以是任何实现 IntoIterator 的类型。

impl MyDynamicType {
    pub fn new<I>(awesome: bool, number: u32, dynamic: I) -> Box<MyDynamicType> 
        where I: IntoIterator<Item = u32>,
              I::IntoIter: ExactSizeIterator,
    {
        // ... implementation details ...
    }
}

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

依赖项

~215KB