Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Using Rust

These examples run inside a Wasm module after postprocessing.

Structs

#[wasmgc] defines a managed struct. Gc::new consumes its initial field values and creates an object on the engine’s GC heap.

use wasmgc::{Gc, GcArray, wasmgc};

#[wasmgc]
struct Counter {
    value: i32,
}

let counter = Gc::new(Counter { value: 1 });
let shared = counter.clone();

shared.set_value(2);
assert_eq!(counter.value(), 2);
assert!(Gc::ref_eq(&counter, &shared));

The macro generates value() and set_value() with the field’s Rust visibility. Cloning creates another root to the same object; both roots observe the same changes. There is no Rust &Counter into the managed object’s storage.

Reference fields

Use Gc<T> for a reference field, or Option<Gc<T>> when it may be null. Reference setters borrow their arguments, and getters return owned roots.

#[wasmgc]
struct Holder {
    counter: Option<Gc<Counter>>,
}

let counter = Gc::new(Counter { value: 3 });
let holder = Gc::new(Holder { counter: None });
holder.set_counter(Some(&counter));

drop(counter);
let retained = holder.counter().unwrap();
assert_eq!(retained.value(), 3);

holder.set_counter(None);
assert_eq!(retained.value(), 3);

The holder keeps its field reachable after the original root is dropped. The getter’s returned root then keeps the counter alive after the field is cleared. The same reference types can form recursive and mutually referencing structs.

Arrays

GcArray<T> is an alias for Gc<Array<T>>. Its length is fixed at construction.

let numbers = GcArray::from_slice([1_i32, 2, 3]);
numbers.set(1, 9);
assert_eq!(numbers.get(1), Some(9));
assert_eq!(numbers.get(3), None);

let zeros = GcArray::<i32>::new_default(4);
assert_eq!(zeros.len(), 4);
assert_eq!(zeros.get(0), Some(0));

Arrays can also hold references. Copying references preserves object identity; it does not copy the objects themselves.

let counter = Gc::new(Counter { value: 5 });
let counters = GcArray::from_slice([counter.clone()]);
counters.set(0, &counter);

drop(counter);
assert_eq!(counters.get(0).unwrap().value(), 5);

from_slice copies elements from a Rust slice, array, or vector into GC storage. It does not transfer the Rust allocation into the GC heap. Array setters panic on an out-of-bounds index.

Reachability

A Gc owns a root. Dropping it releases that root; the object remains alive while another root, host reference, or reachable GC object retains it. The engine can collect unreachable cycles.

Managed objects do not run Rust destructors. Their Rust struct supplies construction values and must not implement Drop. Ordinary Rust allocations, such as Vec and String, remain in linear memory and cannot be stored as GC fields directly.