4个版本

0.3.0 2024年4月21日
0.2.3 2023年11月17日
0.2.2 2023年7月19日
0.1.0 2023年7月16日

内存管理 中排名 99

Download history 158/week @ 2024-04-19 11/week @ 2024-04-26 4/week @ 2024-05-17 2/week @ 2024-05-24 5/week @ 2024-07-05

每月下载量 374

MIT/Apache

82KB
1K SLoC

pared: Projected Shared pointers

License crates.io docs.rs

为Arc和Rc提供投影,仅允许暴露来自T的引用。

包含存储在 std::sync::Arcstd::rc::Rc 中的数据投影的引用计数指针。这是一种类似于 ouroborosyoke 的“自引用”类型。该软件包专门支持 ArcRc,以及从中获取的字段引用,这使得它能够提供比通用自引用软件包更简单的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::Parcsync::Weakprc::Prcprc::Weak 存储它们各自底层的共享指针的类型擦除版本,这使得我们可以调用这些方法来操作底层的 ArcRcWeak

当我们构建 ArcRc 等的类型擦除版本时,我们存储了一个指向包含每个操作函数指针的 const 结构的引用。由于我们总是从一个具体的 Arc<T>Rc<T> 构建出来,我们可以存储一个指向通用辅助类型 const 变量的引用,它首先将类型擦除的指针转换回 Arc<T>Rc<T> 或正确的 Weak<T>,然后调用适当的函数。

我们将底层指针存储为我们从 Arc::into_rawRc::into_raw 获得的指针,在一个结构体中,该结构体将此(可能为 ?Sized)指针存储在 MaybeUninit<[*const ();2]> 中(感谢 Alice Ryhl论坛)。这使得我们能够透明地存储此指针并在具体实现函数中检索它。

顺便提一下,“prc”在捷克语中是放屁的声音,类似于英语中的“toot”。这是为 ParcPrc 命名约定的动机大约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

无运行时依赖