4个版本
0.3.0 | 2024年4月21日 |
---|---|
0.2.3 | 2023年11月17日 |
0.2.2 | 2023年7月19日 |
0.1.0 |
|
在 内存管理 中排名 99
每月下载量 374
82KB
1K SLoC
pared: Projected Shared pointers
为Arc和Rc提供投影,仅允许暴露来自T的引用。
包含存储在 std::sync::Arc
或 std::rc::Rc
中的数据投影的引用计数指针。这是一种类似于 ouroboros 或 yoke 的“自引用”类型。该软件包专门支持 Arc
和 Rc
,以及从中获取的字段引用,这使得它能够提供比通用自引用软件包更简单的API。Parc可以在我们想要公开引用计数指针中存储的数据的一部分,同时仍然保持相同的数据共享所有权的情况下非常有用。我们将存储数据中的一个字段投影到Parc中,使我们只能向接收者公开该数据。
如果提供了 alloc
,则此软件包可以在 no_std
环境中使用。
用法
此库中的指针在需要共享数据所有权(例如在线程间发送数据时),但只想向部分代码公开存储数据的部分时非常有用。
use pared::sync::Parc;
#[derive(Debug)]
struct PublicData;
// No Debug
struct SensitiveData;
struct Data {
public: PublicData,
sensitive: SensitiveData,
}
let data = Parc::new(Data { public: PublicData, sensitive: SensitiveData });
let public_only = data.project(|data| &data.public);
std::thread::spawn(move ||
println!("I can only access public data: {:?}", public_only)
).join().unwrap();
在使用 Parc<T>
或 Prc<T>
时,其底层的 Arc<U>
或 Rc<U>
被类型擦除,因此您可以互换来使用这些指针的实例。
use pared::sync::Parc;
let use_second = true;
let from_tuple = Parc::new((0u8, 1u8, 2u8)).project(|tuple|
if use_second {
&tuple.1
} else {
&tuple.0
}
);
let from_u8 = Parc::new(4u8);
fn check_equal(number: Parc<u8>, reference: u8) {
assert_eq!(*number, reference);
}
let use_from_tuple = true;
if use_from_tuple {
check_equal(from_tuple, 1);
} else {
check_equal(from_u8, 4);
}
背景
C++ 的 std::shared_ptr 有一个 别名构造函数 (8),允许您重用现有共享指针的引用计数,但指向新的数据。这个操作是不安全的,因为 C++ 没有一种方法来限制您使用这个构造函数与指向局部变量的指针。
使用 Rust,我们可以通过安全的 API 提供相同的操作。Rust 的 Arc
不存储引用计数和数据指向的两个不同的指针,所以它不包含这个功能。
我们可以通过在 Arc
(以及 Rc
)周围实现一个包装类型来实施这种“别名 Arc”。由于我们不关心 Arc
中存储的原始数据,我们可以将 Arc 类型擦除为一个不透明指针,并且我们只需要能够调用不依赖于原始 T
的成员函数。
- clone(用于增加计数器)
- drop(用于减少计数器)
- downgrade(用于获取
Weak
指针) - strong_count 和 weak_count
同样地,对于 Weak
,我们只需要
- clone(用于增加弱计数器)
- drop(用于减少弱计数器)
- upgrade(用于获取
Option<Arc>
) - strong_count 和 weak_count
我们的包装类型 sync::Parc
、sync::Weak
、prc::Prc
和 prc::Weak
存储它们各自底层的共享指针的类型擦除版本,这使得我们可以调用这些方法来操作底层的 Arc
、Rc
和 Weak
。
当我们构建 Arc
、Rc
等的类型擦除版本时,我们存储了一个指向包含每个操作函数指针的 const
结构的引用。由于我们总是从一个具体的 Arc<T>
或 Rc<T>
构建出来,我们可以存储一个指向通用辅助类型 const
变量的引用,它首先将类型擦除的指针转换回 Arc<T>
、Rc<T>
或正确的 Weak<T>
,然后调用适当的函数。
我们将底层指针存储为我们从 Arc::into_raw
或 Rc::into_raw
获得的指针,在一个结构体中,该结构体将此(可能为 ?Sized
)指针存储在 MaybeUninit<[*const ();2]>
中(感谢 Alice Ryhl 从 论坛)。这使得我们能够透明地存储此指针并在具体实现函数中检索它。
顺便提一下,“prc”在捷克语中是放屁的声音,类似于英语中的“toot”。这是为 Parc
和 Prc
命名约定的动机大约20%。
替代方案
如果这种投影太简单(例如,您希望存储结构体指针的子集而不是单个指针),对于 T: Sized
类型,应该使用 yoke。
致谢
- Christopher Durham,他提供了关于先例的信息,并概述了初稿的问题
- Alice Ryhl,他解决了如何透明地存储指向
?Sized
类型的指针的问题 - Frank Stefahn,他审阅了原始API想法,并发现了与
Debug
相关的难以发现的正确性问题
许可
© 2023 Radek Vít [[email protected]]。
该项目根据您的选择受以下任一许可协议保护:
。
此项目的SPDX许可证标识符为 MIT OR Apache-2.0
。