9个版本
0.2.3 | 2019年4月12日 |
---|---|
0.2.2 | 2019年3月17日 |
0.1.4 | 2019年3月12日 |
#120 in 无标准库
每月 31 次下载
用于 threadstack
41KB
535 行
rel-ptr
rel-ptr
是一个用于相对指针的库,可以用来创建可移动自引用类型。此库受到了Jonathan Blow在Jai上的工作的启发,他在Jai中添加了相对指针作为基本类型。
相对指针是一种使用偏移量和当前位置来计算其指向位置的指针。
最低Rust版本 = 1.34.0
安全性
有关安全信息,请参阅RelPtr
类型文档
功能
no_std
此crate兼容no-std
,只需添加功能no_std
即可进入no_std
模式。
nightly
使用nightly,您可以使用相对指针使用trait对象
示例
取下面的内存段
[.., 0x3a, 0x10, 0x02, 0xe4, 0x2b ..]
其中 0x3a
的地址是 0xff304050
(32位系统),那么 0x2b
的地址是 0xff304054
。
如果我们有一个1字节相对指针 (RelPtr<_, i8>
),地址为 0xff304052
,那么这个相对指针也指向 0x2b
,这是因为它的地址 0xff304052
加上它的偏移量 0x02
指向 0x2b
。
这里有几点有趣的事情
- 只需1个字节即可指向另一个值,
- 相对指针不能访问所有内存,只能访问附近的内存
- 如果相对指针和被指向的对象一起移动,那么相对指针不会被无效化
第三点使得可移动自引用结构体成为可能
类型 RelPtr<T, I>
是一个相对指针。 T
表示它所指向的内容,而 I
表示它用来存储偏移量。在实际应用中,你可以忽略 I
,因为它默认为 isize
,这将覆盖你使用相对指针的所有情况。但是,如果你想优化指针的大小,你可以使用任何实现了 Delta
的类型。一些来自 std 的实现类型包括:i8
、i16
、i32
、i64
、i128
和 isize
。请注意,权衡是,随着你减小偏移量的大小,你指向的范围也会减小。 isize
至少覆盖了一半的地址空间,所以除非你做了什么非常疯狂的事情,它应该可以工作。对于自引用的结构体,使用一个最大值至少与你的结构体一样大的类型。即 std::mem::size_of::<T>() <= I::max_value()
。
关于 usized 类型的说明:这些类型更难以实现
自引用类型示例
struct SelfRef {
value: (String, u32),
ptr: RelPtr<String, i8>
}
impl SelfRef {
pub fn new(s: String, i: u32) -> Self {
let mut this = Self {
value: (s, i),
ptr: RelPtr::null()
};
this.ptr.set(&mut this.value.0).unwrap();
this
}
pub fn fst(&self) -> &str {
unsafe { self.ptr.as_ref_unchecked() }
}
pub fn snd(&self) -> u32 {
self.value.1
}
}
let s = SelfRef::new("Hello World".into(), 10);
assert_eq!(s.fst(), "Hello World");
assert_eq!(s.snd(), 10);
let s = Box::new(s); // force a move, note: relative pointers even work on the heap
assert_eq!(s.fst(), "Hello World");
assert_eq!(s.snd(), 10);
这个示例是人为构造的,仅作为示例。在这个示例中,我们可以看到一些重要的安全可移动自引用类型的部分,让我们逐个分析。
首先,SelfRef
的定义,它包含一个值和一个相对指针,相对指针将指向 SelfRef.value
中的元组内部,指向 String
。没有生命周期涉及,因为它们要么会使 SelfRef
不可移动,要么无法正确解析。
在 SelfRef::new
中我们看到一个模式,首先创建对象,并使用哨兵 RelPtr::null()
,然后立即使用 RelPtr::set
分配值并解包结果。这个解包操作可以快速得到反馈,以确定指针是否已设置,如果没有设置,则可以增加偏移量的大小并解决它。
一旦指针设置,移动结构体仍然是安全的,因为它使用的是 相对 指针,所以它所在的位置无关紧要,只有它相对于其指针的目标的偏移量。在 SelfRef::fst
中,我们使用 RelPtr::as_ref_unchecked
,因为它不可能使指针无效。这是不可能的,因为我们不能直接设置相对指针,也不能在设置相对指针后更改 SelfRef
字段偏移量。
发行说明
0.2.3
变更
- 由于 rustc 版本 1.34.0,已将
NonZero*
移出夜间版本
0.2.2
移除
- 移除了对
unreachable
的依赖- 在存在
std::hint::unreachable
的情况下是不必要的,并且使用std
版本更安全,因为std
版本保证会被优化掉 - 在调试模式下表现与预期不同,新实现会在调试模式下崩溃,并在发布模式下被优化掉
- 在存在
0.2.1
新增功能
- 关于
Nullable
的文档以及它与Delta
的交互
变更
- 修复了可变性错误,获取原始指针(
*mut T
)或非可空指针(NonNull<T>
)时,应在RelPtr
上获取独占锁
0.2.0
新增功能
- 在
TraitObject
上添加了构造函数,现在有from_ref
和from_mut
以便更容易地在TraitObject
之间进行转换 - 更多文档
移除
Default
对于MetaData::Data
的限制- 现在在设置相对指针之前访问
MetaData::Data
是未定义行为的
- 现在在设置相对指针之前访问
- 建议使用新的构造函数而不是
TraitObject::new
变更
- 重构了
MetaData::decompose
- 更改为
MetaData::data
,可以通过指针转换提取指针,因此只需要数据
- 更改为
- 将
MetaData
转换为使用std::mem::NonNull
,因为它更容易处理- 这是由于使用
Option<NonNull<T>>
允许在T: !Sized
的情况下表示空值
- 这是由于使用
注释
我不预期 API 将有任何大规模的变化,因此这应该是最终的 API。在发布 1.0.0 之前,我将等待并查看是否有任何错误。我还需要等待关于类型布局的这次 GitHub 讨论的结果,因为它与移动自引用类型的安全性模型有关。
0.1.4
新增功能
- 对
NonZero*
整数的支持 - 格式化支持格式化的所有
RelPtr
变更
-
将 API 转换为使用
&mut T
而不是&T
- 这更好地表示了
RelPtr
的语义,并由 Yandros 建议
- 这更好地表示了
-
将
Delta::ZERO
移动到Nullable::NULL
- 这是为了支持
NonZero*
类型
- 这是为了支持
-
更新文档以更好地解释可能的未定义行为
-
从
TraitObject::into
更改为TraitObject::as_ref
和TraitObject::as_mut