# pared: ***P***rojected Sh***ared*** pointers
[](COPYRIGHT.md)
[](https://crates.io/crates/pared)
[](https://docs.rs/pared)
Projections for Arc and Rc that allow exposing only references that come from T.
Reference-counted pointers that contain projections of data stored in [`std::sync::Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)
or [`std::rc::Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html).
This is a "self-referential" type in the vein of [ouroboros](https://lib.rs/ouroboros)
or [yoke](https://lib.rs/yoke).
This crate specializes to only supporting `Arc` and `Rc` and only references to fields
obtainable from them, which allows it to provide a much simpler API compared to general
self-referential crates.
Parc can be useful in situations where we want to expose only a part of data stored
in a reference-counted pointer while still retaining the same shared ownership of that data.
We project a field from our stored data to store in Parc, allowing us to only expose that data
to the receiver.
This crate can be used in `no_std` environments, given that `alloc` is available.
## Usage
Pointers from this library can be useful in situations where you're required to share ownership of
data (e.g. when sending it between threads), but only want to expose a part of the stored data
to a part of the code.
```rust
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();
```
When using `Parc` or `Prc`, the underlying `Arc` or `Rc` is type-erased, so you can use instances of these pointers interchangeably:
```rust
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, 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);
}
```
## Background
C++'s [std::shared_ptr](https://en.cppreference.com/w/cpp/memory/shared_ptr) has an
[aliasing constructor (8)](https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr)
that lets you reuse the existing reference count of that shared pointer, but point to
new data.
This operation is unsafe, since C++ doesn't have a way to restrict you from using this constructor
with a pointer to a local variable.
With Rust, we can expose the same operation with a safe API.
Rust's [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html) doesn't store two discinct
pointers to the reference count and the data, so it doesn't include this functionality
out of the box.
We can implement this "aliasing Arc" as a wrapper type around `Arc` (and `Rc`).
Since we're not interested in the original data stored in the `Arc`, we can type-erase the Arc
into an opaque pointer, and we only need to be able to call member functions that don't depend
on the original `T`:
- clone (to increment the counter)
- drop (to decrement the counter)
- downgrade (to get the `Weak` pointer)
- strong_count and weak_count
Similarly, for `Weak`, we only need
- clone (to increment the weak counter)
- drop (to decrement the weak counter)
- upgrade (to get `Option`)
- strong_count and weak_count
Our wrapper types `sync::Parc`, `sync::Weak`, `prc::Prc` and `prc::Weak` store type-erased versions of their respective underlying shared pointers, which allows us to call these methods
on the underlying `Arc`s, `Rc`s and `Weak`s.
When we construct the type-erased versions of `Arc`, `Rc`, etc., we store a reference to a `const` structure with function pointers to each of those operations. Since we always construct from a concrete `Arc` or `Rc`, we can store a reference to a generic helper type's `const` variable that first converts the type-erased pointer back to `Arc`, `Rc`, or the correct `Weak`, and then calls the appropriate function.
We store the underlying pointer as a pointer we obtain from
[`Arc::into_raw`](https://doc.rust-lang.org/std/sync/struct.Arc.html#method.into_raw)
or
[`Rc::into_raw`](https://doc.rust-lang.org/std/rc/struct.Rc.html#method.into_raw)
in a structure that stores this (potentially `?Sized`) pointer in a
`MaybeUninit<[*const ();2]>` (credit to [Alice Ryhl](https://users.rust-lang.org/u/alice)
from the [forum](https://users.rust-lang.org)).
This allows us to transparently store this pointer and retreive it back inside the concrete implementation functions.
As an aside, "prc" is the sound of farting in Czech, similar to "toot" in English.
This is around 20% of the motivation behind the naming convention for `Parc` and `Prc`.
## Alternatives
If this kind of projection is too simple (e.g. you'd like to store a subset of a `struct`'s pointers instead of just one), [yoke](https://lib.rs/yoke) should do the trick for `T: Sized` types.
## Acknowledgments
- [Christopher Durham](https://users.rust-lang.org/u/cad97/summary) for
[giving information](https://users.rust-lang.org/t/could-arc-have-arc-aliased-that-would-behave-like-c-shared-ptrs-aliasing-constructor/96924/5)
about prior art and outlining issues with first drafts
- [Alice Ryhl](https://users.rust-lang.org/u/alice) for
[solving](https://users.rust-lang.org/t/type-erasing-pointers-to-t-sized/96984/3)
how to transparently store pointers to `?Sized` types
- [Frank Stefahn](https://users.rust-lang.org/u/steffahn) for reviewing the original API ideas
and spotting a difficult-to-spot soundness hole with `Debug`
## License
© 2023 Radek Vít [radekvitr@gmail.com].
This project is licensed under either of
- [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) ([`LICENSE-APACHE`](LICENSE-APACHE))
- [MIT license](https://opensource.org/licenses/MIT) ([`LICENSE-MIT`](LICENSE-MIT))
at your option.
The [SPDX](https://spdx.dev) license identifier for this project is `MIT OR Apache-2.0`.