Skip to main content

gc_lang/
lib.rs

1//! # gc_lang
2//!
3//! A tracing garbage collector for interpreted-language runtimes.
4//!
5//! `gc-lang` gives a runtime one focused thing: a heap it can allocate objects into
6//! and periodically sweep, reclaiming everything no longer reachable — cycles
7//! included. Allocate a value into a [`Heap`] and get back a [`Gc`] handle: a small,
8//! `Copy`, generation-stamped reference that objects store to point at one another.
9//! When the runtime hands its roots to [`Heap::collect`], the collector traces the
10//! graph those roots reach and frees the rest.
11//!
12//! It is a mark-and-sweep collector, and it is entirely safe — the crate is
13//! `#![forbid(unsafe_code)]`. Two design choices carry that:
14//!
15//! - **Reachability, not reference counting.** Collection is decided by tracing from
16//!   roots, so unreachable cycles are reclaimed. A runtime can build arbitrary
17//!   object graphs — doubly-linked structures, back-edges, self-references — without
18//!   leaking them.
19//! - **Handles, not pointers.** A [`Gc`] is an index plus a generation stamp, not an
20//!   address. Objects wire to each other by handle, so the graph never fights the
21//!   borrow checker, and a handle to a collected object resolves to `None` instead
22//!   of dangling. There is no use-after-free to have.
23//!
24//! It owns object storage and reclamation only — no value representation, no
25//! interpreter, no scheduling of when to collect. The runtime decides what its roots
26//! are and when a collection is worth its cost.
27//!
28//! ## The `Trace` contract
29//!
30//! An object type becomes collectable by implementing [`Trace`]: during the mark
31//! phase the collector calls [`Trace::trace`], and the object reports each [`Gc`]
32//! handle it owns by calling [`Tracer::mark`]. Marking a handle keeps its object
33//! alive; a handle you hold but never mark is treated as unreachable. Leaf objects
34//! that own no handles implement `trace` as an empty body.
35//!
36//! ## Quickstart
37//!
38//! A two-object graph: reclaim the unreachable one, keep the rooted one.
39//!
40//! ```
41//! use gc_lang::{Gc, Heap, Trace, Tracer};
42//!
43//! // The runtime's object type. It owns handles to other objects; `trace` reports them.
44//! enum Value {
45//!     Number(f64),
46//!     Pair(Gc<Value>, Gc<Value>),
47//! }
48//!
49//! impl Trace for Value {
50//!     fn trace(&self, tracer: &mut Tracer<'_>) {
51//!         if let Value::Pair(a, b) = self {
52//!             tracer.mark(*a);
53//!             tracer.mark(*b);
54//!         }
55//!     }
56//! }
57//!
58//! let mut heap = Heap::new();
59//! let one = heap.alloc(Value::Number(1.0));
60//! let two = heap.alloc(Value::Number(2.0));
61//! let pair = heap.alloc(Value::Pair(one, two));
62//! let unused = heap.alloc(Value::Number(3.0));
63//! assert_eq!(heap.len(), 4);
64//!
65//! // Collect with `pair` as the only root. `pair`, `one`, and `two` are reachable;
66//! // `unused` is not.
67//! let stats = heap.collect([pair]);
68//! assert_eq!(stats.freed, 1);
69//! assert!(heap.get(unused).is_none());
70//! assert!(heap.get(one).is_some());
71//! ```
72//!
73//! ## `no_std`
74//!
75//! The crate is `no_std` by default-compatible: it needs only `alloc`, and the
76//! `std` feature (on by default) links the standard library without changing the
77//! public surface. Disable default features to build in a `no_std` environment.
78
79#![cfg_attr(not(feature = "std"), no_std)]
80#![cfg_attr(docsrs, feature(doc_cfg))]
81#![forbid(unsafe_code)]
82#![deny(missing_docs)]
83#![deny(unused_must_use)]
84#![deny(unused_results)]
85#![deny(clippy::unwrap_used)]
86#![deny(clippy::expect_used)]
87#![deny(clippy::todo)]
88#![deny(clippy::unimplemented)]
89#![deny(clippy::print_stdout)]
90#![deny(clippy::print_stderr)]
91#![deny(clippy::dbg_macro)]
92#![deny(clippy::unreachable)]
93
94extern crate alloc;
95
96mod error;
97mod handle;
98mod heap;
99mod trace;
100
101pub use error::GcError;
102pub use handle::Gc;
103pub use heap::{CollectStats, Heap};
104pub use trace::{Trace, Tracer};