1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! Reachability tracing: the [`Trace`] contract and the [`Tracer`] sink.
extern crate alloc;
use Vec;
use crateGc;
/// Reports the outgoing [`Gc`] edges of a heap object so the collector can follow
/// them.
///
/// A mark-and-sweep collector keeps an object alive by reaching it from a root.
/// "Reaching" means walking the graph of handles one object holds to the next, and
/// only the object itself knows which handles those are — they might be struct
/// fields, vector elements, map values, or buried in an enum variant. `Trace` is
/// how the object hands that list to the collector: [`Heap::collect`] calls
/// [`trace`](Trace::trace) on every object it visits, and the object calls
/// [`Tracer::mark`] once per handle it stores.
///
/// The contract is small but load-bearing:
///
/// - **Mark every owned handle.** A handle you hold but do not mark is invisible to
/// the collector; the object it points at can be swept while you still hold a
/// handle to it. That is not unsound — the stale handle simply resolves to `None`
/// afterwards — but it is almost never what you want. Marking is how you say
/// "keep this alive".
/// - **Marking more than you own is safe.** Extra marks at worst keep an object
/// alive one cycle longer. The collector already ignores handles that do not name
/// a live object, so a stale handle you mark costs nothing.
/// - **`trace` must not allocate into the heap or mutate the object.** It is a
/// read-only enumeration; it takes `&self` for exactly that reason.
///
/// Leaf objects that hold no handles implement `trace` as an empty body.
///
/// [`Heap::collect`]: crate::Heap::collect
///
/// # Examples
///
/// A cons-cell value type for a small interpreter. Each variant reports the handles
/// it owns and nothing else:
///
/// ```
/// use gc_lang::{Gc, Trace, Tracer};
///
/// enum Value {
/// Nil,
/// Int(i64),
/// Pair(Gc<Value>, Gc<Value>),
/// List(Vec<Gc<Value>>),
/// }
///
/// impl Trace for Value {
/// fn trace(&self, tracer: &mut Tracer<'_>) {
/// match self {
/// // Leaves own no handles.
/// Value::Nil | Value::Int(_) => {}
/// // A pair owns its two children.
/// Value::Pair(car, cdr) => {
/// tracer.mark(*car);
/// tracer.mark(*cdr);
/// }
/// // A list owns each element.
/// Value::List(items) => {
/// for item in items {
/// tracer.mark(*item);
/// }
/// }
/// }
/// }
/// }
/// ```
/// The sink a [`Trace`] implementation reports its outgoing edges to.
///
/// A `Tracer` is handed to [`Trace::trace`] during a collection. Its one job is to
/// receive handles: each call to [`mark`](Tracer::mark) records that the object
/// being traced points at another, so the collector can visit it in turn. It holds
/// a borrow of the collector's work queue and nothing else, so recording an edge is
/// a single push — no allocation on the steady-state path, since the queue is
/// reused across collections.
///
/// You never construct a `Tracer`; the heap builds one and passes it in.