Dynace-RS is a Rust crate family that adds a Dynace-/Smalltalk-style dynamic object system on top of Rust, including:
- full object-oriented capabilities, including
- classes, instances, methods, & generic functions
- encapsulation
- polymorphism
- inheritance (single and multiple, with C3 linearization)
- fully meta-class based (like Smalltalk)
- dynamic binding and dispatching through generic functions (like CLOS)
- class variables shared across the class hierarchy (Smalltalk semantics)
- full reflection of classes, metaclasses, variables, methods, and generics
- an automatic code generator that scans your crate and emits one typed
Rust wrapper per generic, so callers write
Area(&mut world, obj, &[])rather thanworld.call_generic(GenericId::from_static("Area"), …)
- written in pure stable Rust (1.80+)
- normal user code refers to classes by Rust marker types, not strings — typos are compile-time errors
- macros (
class!,method!,register!) hide the runtime plumbing behind a small, readable surface - twelve self-contained example crates progressing from a one-class "hello, object" to a multiple-inheritance shape system
- full tutorial and reference manual in Texinfo, building to both HTML and PDF
cargo testkeeps the runtime, macros, generator, and examples honest — 103 tests across the workspace, zero warnings
Dynace-RS is a copy of the ideas from Dynace, a portable open-source extension to the C language that adds an object-oriented runtime with classes, generic functions, multiple inheritance, garbage collection, and threads. The original Dynace was fine-tuned over many years of commercial use; this crate brings the same object model into Rust, replacing the C preprocessor magic with proc macros and bolting onto the Rust type system wherever it makes sense.
Concepts that carry over directly: every entity is an object; classes
and instances share the same dispatch path; metaclasses parallel the
class hierarchy; methods are private and the public interface is the
set of generated generics. Concepts that change: there is no tracing
GC (the World owns the heap); thread support is intentionally out of
scope for the current version; the cargo build / cargo test
workflow replaces the makefile-driven C build.
Three layers of material, depending on how you like to read:
- A hands-on tutorial.
docs/dynace-tutorial.texiwalks through the system in narrative order, one chapter per concept, with code that maps directly to the example crates below. - A reference manual.
docs/dynace-manual.texiis the API-and-concept reference, with a full error catalogue and design-rationale appendix. - Twelve runnable example crates. Everything under
examples/, each a self-contained Cargo crate with its ownreadme.txt. They progress from01-hello-objectto a multiple-inheritance shape system in12-mini-shape-system. See the Examples section below for the full list.
The tutorial and manual are written in Texinfo; build them to HTML and
PDF with make -C docs all. The example crates run individually with
cargo run -p example-NN-name (and have per-directory Makefiles).
use dynace::{GenericId, ObjectClass, ObjectRef, Result, Value, World};
dynace::class! { class AnimalClass : ObjectClass {} }
dynace::class! { class DogClass : AnimalClass {} }
dynace::class! { class CatClass : AnimalClass {} }
dynace::method! {
impl AnimalClass {
fn Speak(_w: &mut World, _r: ObjectRef) -> Result<Value> {
Ok(Value::from("some generic animal noise"))
}
}
impl DogClass {
fn Speak(_w: &mut World, _r: ObjectRef) -> Result<Value> {
Ok(Value::from("woof"))
}
}
}
fn main() -> Result<()> {
let mut world = World::new();
dynace::register!(&mut world,
AnimalClass,
DogClass,
CatClass,
AnimalClass::Speak,
DogClass::Speak,
);
let rex = world.new_instance_typed::<DogClass>()?;
let whiskers = world.new_instance_typed::<CatClass>()?;
let speak = GenericId::from_static("Speak");
println!("Dog says: {:?}", world.call_generic(speak, rex, &[])?);
println!("Cat says: {:?}", world.call_generic(speak, whiskers, &[])?);
Ok(())
}The cat falls through AnimalClass's implementation because dispatch
walks the class precedence list and the dog's override wins for any
DogClass instance.
dynace— the runtime: the object heap, class/metaclass/method layer, generic dispatch, and reflection.dynace-macros—class!,method!, andregister!procedural macros. Re-exported throughdynace.dynace-gen— the code generator: scans crate source, merges with optional TOML databases from upstream Dynace-RS systems, emits one typed wrapper per generic. Available as a library and as a CLI (dynace-gen).dynace-build— a thin build-script helper arounddynace-gen, designed for[build-dependencies].
Twelve self-contained example crates live under examples/.
Each is a real Cargo crate with its own Cargo.toml, src/main.rs,
Makefile (targets build / run / clean), and readme.txt. They
progress from the simplest possible runtime use to the full macro +
generator pipeline:
| Directory | Demonstrates |
|---|---|
01-hello-object |
One class, one method, one dispatched call |
02-define-a-class |
Class identity and class_of |
03-instance-variables |
Per-instance state |
04-class-variables |
Class-wide shared state |
05-single-inheritance |
Inherited instance and class variables |
06-multiple-inheritance |
Diamond, C3, is_kind_of |
07-private-methods |
Method dispatch and override |
08-class-side-methods |
Factory + class-wide counter (advanced) |
09-polymorphism |
One generic, many class implementations |
10-generated-generics |
build.rs + dynace-build + generated wrapper |
11-reflection |
Full reflection dump (advanced) |
12-mini-shape-system |
Capstone: MI + ivars + cvars + class-side New |
Each example builds individually — they are not part of the
default workspace build, so cloning the repo and running cargo build
does not pull them in. Run one with
cargo run -p example-NN-name # from the workspace root
# or
make -C examples/NN-name run # from inside the example directoryEach example can also be copied to any directory on disk and built
standalone after editing one dependency path in the example's
Cargo.toml. The example's own readme.txt documents that workflow.
Two book-length documents live under docs/:
- Tutorial —
docs/dynace-tutorial.texi. A progressive hands-on guide that mirrors the example progression. Read this front-to-back if you are new to the system. - User manual —
docs/dynace-manual.texi. The reference, organized by concept, with a full API surface, error catalogue, and design-rationale appendix. Read this when you know what you're looking for.
Both are Texinfo sources that build to HTML and PDF via the standard GNU toolchain:
make -C docs html # → docs/build/html/{dynace-tutorial,dynace-manual}/index.html
make -C docs pdf # → docs/build/pdf/{dynace-tutorial,dynace-manual}.pdf
make -C docs all # both
make -C docs clean(requires texinfo and a TeX distribution such as texlive-scheme-basic).
Dynace-RS_design.md at the repository root is
the authoritative design document — the precise specification of every
API, every error, and every invariant. Useful as a third reading after
the tutorial and the manual.
A top-level Makefile wraps the two underlying build systems (Cargo
for the Rust crates, Texinfo for the docs):
make build # build the four library crates
make test # run the full test suite (103 tests)
make docs # build HTML + PDF copies of the tutorial and manual
make all # build + docs
make clean # wipe everything generated (target/ and docs/build/)Equivalent direct commands:
cargo build # → make build
cargo test # → make test
make -C docs all # → make docs
cargo clean && make -C docs clean # → make clean
cargo run -p example-01-hello-object # run a specific example
make -C examples/01-hello-object run # same thing, from inside the exampleThe workspace's default-members list excludes the examples, so neither
make build / cargo build nor make test / cargo test ever compile
example code. Each example builds only when you name it explicitly with
-p, or from inside its own directory.
Released under the BSD 2-Clause license — same terms as the original
Dynace. See LICENSE.txt for the full text.
The Dynace object system — the design itself, the object model, the generic-function dispatch, the shared-class-variable semantics, all of it — was conceived by Blake McBride and refined over many years of commercial use in C; see the original at github.com/blakemcbride/Dynace.
This Rust implementation — the dynace, dynace-macros,
dynace-gen, and dynace-build crates, plus the twelve example
crates and the Texinfo tutorial and reference manual under docs/ —
was written by Claude Code,
Anthropic's command-line agent, working from Blake's design
specification (Dynace-RS_design.md).
Dynace-RS reimplements the core ideas of Dynace in Rust. The original Dynace was used in production for over a decade; this Rust port preserves the object model (classes/metaclasses/generics, single + multiple inheritance, shared class variables, reflection) and the "methods are private, generics are public" discipline that makes Dynace-style systems pleasant to extend.
If you came here from the C Dynace and you're looking for the
documentation that explains why the system is shaped this way, the
Dynace user manual (PDF)
is still the best starting point. The Rust manual under docs/
focuses on the Rust API surface.
Commercial support is available from blake@mcbridemail.com.