7 个不稳定版本 (3 个重大变更)
0.3.3 | 2021 年 10 月 4 日 |
---|---|
0.3.2 | 2021 年 10 月 4 日 |
0.3.0 | 2021 年 9 月 30 日 |
0.2.0 | 2021 年 9 月 30 日 |
0.0.0 | 2021 年 9 月 27 日 |
#1750 在 Rust 模式 中
每月 29 次下载
40KB
608 行
Thinnable
标准的 Rust DST 引用不仅包含对底层对象的指针,还包含一些关联元数据,这些元数据可以用于在运行时解析 DST。指针和元数据共同存储在所谓的“胖”或“宽”引用中,这通常是人们想要的:因此,可以通过直接内存访问来访问引用,并且每个引用都可以引用单个底层对象的不同 DST(例如,切片或特质)。
然而,为了存储相同 DST 引用的多个副本,通常要么在每一个这样的引用上重复元数据,要么进行一些昂贵的间接引用;此外,在某些情况下,Rust 的元数据可能被认为是过度的:一些其他(较小的)类型可能就足够了(例如,对于特定的切片,我们可能知道它们的长度始终可以适应一个 u8
,因此使用一个 usize
作为它们的元数据是不必要的;或者,对于特定的特质对象,我们可能知道底层类型始终来自某个 枚举
,因此使用 vtable 指针作为它们的元数据是不必要的)。解决这些关注点可以节省宝贵的内存,尤其是在同时使用大量此类引用的情况下。
Thinnable
存储的是与引用一起的元数据,而不是存储引用本身,因此允许用户获得对(现在已装饰了元数据的)对象的“瘦”DST引用:即,ThinRef
和ThinMut
,分别用于共享和独占引用。但这样做是以额外的间接内存访问为代价来获取元数据的,而不是直接在栈上可用;因此,就像经常发生的那样,我们是在时间和空间之间进行权衡。此外,对于任何给定的Thinnable
,其“瘦”引用只能指向与该Thinnable
实例化时的一个DST(尽管如果需要,仍然可以获取其DST的常规“胖”引用)。
可以指定非标准元数据类型,例如,当切片适合其边界时,可以使用较小的整数(如u8
)代替默认的usize
:如果元数据不能转换为所提出的类型,将出现MetadataCreationFailure
。使用此类非标准元数据类型可以节省一些存储空间,但显然在每次解引用时都会增加额外的转换开销。
提供了ThinnableSlice<T>
类型别名,以更方便地使用[T]
切片;并且ThinnableSliceU8<T>
、ThinnableSliceU16<T>
和ThinnableSliceU32<T>
别名提供了与[T]
切片相同的方便使用,但分别将长度元数据编码在u8
、u16
或u32
中。
此crate目前依赖于Rust的不可稳定ptr_metadata
和unsize
功能,并相应地需要一个nightly工具链。
示例
use core::{alloc::Layout, convert::TryFrom, fmt, mem};
use thinnable::*;
const THIN_SIZE: usize = mem::size_of::<&()>();
// Creating a thinnable slice for an array is straightforward.
let thinnable_slice = ThinnableSlice::new([1, 2, 3]);
// Given a thinnable, we can obtain a shared reference...
let r = thinnable_slice.as_thin_ref(); // ThinRef<[u16]>
// ...which is "thin"....
assert_eq!(mem::size_of_val(&r), THIN_SIZE);
// ...but which otherwise behaves just like a regular "fat" DST
// reference
assert_eq!(&r[..2], &[1, 2]);
// For types `M` where the metadata type implements `TryInto<M>`, we
// can use `Thinnable::try_new` to try creating a thinnable using
// `M` as its stored metadata type (dereferencing requires that
// `M: TryInto<R>` where `R` is the original metadata type).
//
// For slices, there's a slightly more ergonomic interface:
let size_default = mem::size_of_val(&thinnable_slice);
let mut thinnable_slice;
thinnable_slice = ThinnableSliceU8::try_slice([1, 2, 3]).unwrap();
let size_u8 = mem::size_of_val(&thinnable_slice);
assert!(size_u8 < size_default);
// We can also obtain a mutable reference...
let mut m = thinnable_slice.as_thin_mut(); // ThinMut<[u16], u8>
// ...which is also "thin"....
assert_eq!(mem::size_of_val(&m), THIN_SIZE);
// ...but which otherwise behaves just like a regular "fat" DST
// reference.
m[1] = 5;
assert_eq!(&m[1..], &[5, 3]);
// We can also have thinnable trait objects:
let thinnable_trait_object;
thinnable_trait_object = Thinnable::<_, dyn fmt::Display>::new(123);
let o = thinnable_trait_object.as_thin_ref();// ThinRef<dyn Display>
assert_eq!(mem::size_of_val(&o), THIN_SIZE);
assert_eq!(o.to_string(), "123");