https://github.com/artichoke/cactusref
🌵 Cycle-Aware Reference Counting in Rust
https://github.com/artichoke/cactusref
artichoke garbage-collection garbage-collector memory-management reference-counting rust rust-crate
Last synced: 5 months ago
JSON representation
🌵 Cycle-Aware Reference Counting in Rust
- Host: GitHub
- URL: https://github.com/artichoke/cactusref
- Owner: artichoke
- License: mit
- Created: 2019-07-19T03:02:01.000Z (over 6 years ago)
- Default Branch: trunk
- Last Pushed: 2025-05-13T00:03:29.000Z (5 months ago)
- Last Synced: 2025-05-13T01:19:51.093Z (5 months ago)
- Topics: artichoke, garbage-collection, garbage-collector, memory-management, reference-counting, rust, rust-crate
- Language: Rust
- Homepage: https://crates.io/crates/cactusref
- Size: 2.89 MB
- Stars: 148
- Watchers: 5
- Forks: 4
- Open Issues: 9
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# CactusRef
[](https://github.com/artichoke/cactusref/actions)
[](https://discord.gg/QCe2tp2)
[](https://twitter.com/artichokeruby)
[](https://crates.io/crates/cactusref)
[](https://docs.rs/cactusref)
[](https://artichoke.github.io/cactusref/cactusref/)Single-threaded, cycle-aware, reference-counting pointers. 'Rc' stands for
'Reference Counted'.> What if, hear me out, we put a hash map in a smart pointer?
CactusRef is a single-threaded, reference-counted smart pointer that can
deallocate cycles without having to resort to weak pointers. [`Rc`][std-rc] from
`std` can be difficult to work with because creating a cycle of `Rc`s will
result in a memory leak.[std-rc]: https://doc.rust-lang.org/stable/std/rc/struct.Rc.html
CactusRef is a near drop-in replacement for `std::rc::Rc` which introduces
additional APIs for bookkeeping ownership relationships in a graph of `Rc`s.Combining CactusRef's [adoption APIs] for tracking links in the object graph and
driving garbage collection with Rust's [drop glue] implements a kind of tracing
garbage collector. Graphs of CactusRefs detect cycles local to the graph of
connected CactusRefs and do not need to scan the whole heap as is [typically
required][rust-tour-tracing-gc] in a tracing garbage collector.Cycles of CactusRefs are deterministically collected and deallocated when they
are no longer reachable from outside of the cycle.[adoption apis]:
https://artichoke.github.io/cactusref/cactusref/trait.Adopt.html
[drop glue]: https://doc.rust-lang.org/nightly/reference/destructors.html
[rust-tour-tracing-gc]:
https://manishearth.github.io/blog/2021/04/05/a-tour-of-safe-tracing-gc-designs-in-rust/## Self-referential Data Structures
CactusRef can be used to implement [self-referential data structures] such as a
doubly-linked list without using weak references.[self-referential data structures]:
https://artichoke.github.io/cactusref/cactusref/implementing_self_referential_data_structures/index.html## Usage
Add this to your `Cargo.toml`:
```toml
[dependencies]
cactusref = "0.5.0"
```CactusRef is mostly a drop-in replacement for `std::rc::Rc`, which can be used
like:```rust
use cactusref::Rc;let node = Rc::new(123_i32);
let another = Rc::clone(&node);
assert_eq!(Rc::strong_count(&another), 2);let weak = Rc::downgrade(&node);
assert!(weak.upgrade().is_some());
```Or start making self-referential data structures like:
```rust
use std::cell::RefCell;
use cactusref::{Adopt, Rc};struct Node {
next: Option>>,
data: i32,
}let left = Node { next: None, data: 123 };
let left = Rc::new(RefCell::new(left));let right = Node { next: Some(Rc::clone(&left)), data: 456 };
let right = Rc::new(RefCell::new(right));unsafe {
// bookkeep that `right` has added an owning ref to `left`.
Rc::adopt_unchecked(&right, &left);
}left.borrow_mut().next = Some(Rc::clone(&right));
unsafe {
// bookkeep that `left` has added an owning ref to `right`.
Rc::adopt_unchecked(&left, &right);
}let mut node = Rc::clone(&left);
// this loop will print:
//
// > traversing ring and found node with data = 123
// > traversing ring and found node with data = 456
// > traversing ring and found node with data = 123
// > traversing ring and found node with data = 456
// > traversing ring and found node with data = 123
for _ in 0..5 {
println!("traversing ring and found node with data = {}", node.borrow().data);
let next = if let Some(ref next) = node.borrow().next {
Rc::clone(next)
} else {
break;
};
node = next;
}
assert_eq!(Rc::strong_count(&node), 3);
drop(node);drop(left);
drop(right);
// All members of the ring are garbage collected and deallocated.
```## Maturity
CactusRef is experimental. This crate has several limitations:
- CactusRef is nightly only.
- Cycle detection requires [unsafe code][adopt-api] to use.CactusRef is a non-trivial extension to `std::rc::Rc` and has not been proven to
be safe. Although CactusRef makes a best effort to abort the program if it
detects a dangling `Rc`, this crate may be unsound.[adopt-api]: https://docs.rs/cactusref/*/cactusref/trait.Adopt.html
## `no_std`
CactusRef is `no_std` compatible with an optional and enabled by default
dependency on `std`. CactusRef depends on the [`alloc`] crate.[`alloc`]: https://doc.rust-lang.org/alloc/
## Crate features
All features are enabled by default.
- **std** - Enable linking to the [Rust Standard Library]. Enabling this feature
adds [`Error`] implementations to error types in this crate.[rust standard library]: https://doc.rust-lang.org/nightly/std/
[`error`]: https://doc.rust-lang.org/nightly/std/error/trait.Error.html## License
CactusRef is licensed with the [MIT License](LICENSE) (c) Ryan Lopopolo.
CactusRef is derived from `Rc` in the Rust standard library @
[`f586d79d`][alloc-rc-snapshot] which is dual licensed with the [MIT
License][rust-mit-license] and [Apache 2.0 License][rust-apache2-license].[alloc-rc-snapshot]:
https://github.com/rust-lang/rust/blob/f586d79d183d144e0cbf519e29247f36670e2076/library/alloc/src/rc.rs
[rust-mit-license]:
https://github.com/rust-lang/rust/blob/f586d79d183d144e0cbf519e29247f36670e2076/LICENSE-MIT
[rust-apache2-license]:
https://github.com/rust-lang/rust/blob/f586d79d183d144e0cbf519e29247f36670e2076/LICENSE-APACHE