1个不稳定版本
0.1.0 | 2022年8月11日 |
---|
#1948 in Rust模式
在swifer中使用
16KB
162 行
dyn_struct2
对dyn_struct
(GitHub,crates.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
,尽管在未来版本中我们可能还会允许 Rc
和 Arc
。在此同时,可以使用 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