| Trait | Interface | |
|---|---|---|
| Typing | Nominal — must be explicitly implemented. | Structural (duck-typed) — automatically satisfied when fields match. |
| Extensible | Yes — via extends, or as a precondition using requires. |
No — interfaces cannot extend each other, though a trait may requires one. |
| Has implementation? | Yes — default method bodies are allowed. | No — declaration only. |
| Field access | Only through a trait that requires the interface. |
Yes — directly, since interfaces are structural. |
A requires Cond clause goes before trait or interface. It forces any implementing type to also satisfy that condition. Traits may provide default method bodies. Use self / other as the conventional instance parameters, and Self (capitalized) for the concrete implementing type.
trait Printable {
fn print(self);
fn println(self) { self.print(); io.writeln(""); }
}
requires Printable
trait JsonSerializable {
fn print(self);
fn toJson(self): string;
}
// Disambiguate overlapping method names using the trait as a namespace
Printable.print(self);
JsonSerializable.print(self);
Interfaces are structural — if it quacks, it's a duck. Any type that has the required fields satisfies the interface automatically, without an explicit implement declaration. You can also add requires to interfaces.
// will only accept structs and reject components
requires @isStruct
interface iPositioned { [x, y, z]: f32 }
requires iPositioned
trait Movable {
fn translate(self, dx: f32, dy: f32, dz: f32) {
self.x += dx; self.y += dy; self.z += dz;
}
}
// Any struct with x, y, z: f32 satisfies iPositioned automatically
struct Enemy { [x, y, z]: f32, health: i32 }
Capabilities are special traits that feed the compiler more information, unlocking special rules and optimizations.
| Capability | What it does |
|---|---|
Copy |
Implicit bitwise copy. Components and primitives are Copy by default. |
Functor |
operator() — makes a type callable like a function. |
Arithmetic |
Operators +, -, *, /, %, and so on. |
Error |
operator throw, required for throw MyError;. |
Null |
A negative capability — its traits activate only once NRA has proven a value IS null. Outside the proven-null branch, calling the method is a compile error. |
Fail |
A negative capability — its traits activate only once NRA has proven a value IS an error. Cannot coexist with Null on the same level, but ?T! can have Null on the outer level and Fail on the inner. |
Allocator |
To provide custom allocators |
Generator |
Allows creating custom Generators & receive the return of an async fn. |
Share |
Required for global: share and crossing thread boundaries |
Lent |
Enables global: unique, a runtime-checked exclusive borrow. global bindings cannot be moved — Lent manages thread-safe distribution. Also allows lend parameters. |
Trust |
A trait extending Trust may contain raw fn methods callable from safe contexts. |
Unique |
Marks a singleton type. It cannot be instantiated — the type name itself acts as the instance. All fields must implement Share (thread-safe). A unique Local variant is a singleton thread-local. |
Null & Fail — Negative CapabilitiesDispatch is based on NRA state. Inside a proven-null branch, the Null trait unlocks. Outside it, calling Null methods is a compile error:
implement Config as Null {
fn onMissing(self) { log("Config was null -- using defaults"); }
}
implement Config! as Fail {
fn onError(self) { log("Config load failed"); }
}
let cfg: ?Config = loadConfig();
if (cfg is null) {
cfg.onMissing(); // OK — Null trait unlocked here
}
// cfg.onMissing(); -- COMPILE ERROR: outside null branch
// Multi-level: ?T! — Null on outer, Fail on inner
// cfg.onError() is only valid inside a proven-error branch
Null and Fail are per-level. A ?T! value can activate Null (outer ?) independently from Fail (inner !).
Trust — Safe Sections with Raw Codetrait Place extends Trust {
raw fn sample(): i32 {}
}
fn safeCaller(a: Place) {
let v = a.sample();
}
Unique — Singleton Typesstruct AppConfig: Unique { host: string, port: u16 }
AppConfig.host = "localhost";
AppConfig.port = 8080;
// let cfg = AppConfig { ... }; -- COMPILE ERROR
Share — Mutable State Across Thread Boundaries// Share is mutable, multiple names, multiple threads, all can write
global counter: share Atomic<i32> = 0;
// Without the Share capability, cross-thread access is a compile error
struct LocalOnly { data: i32 }
// global bad: share LocalOnly = ...; -- COMPILE ERROR: lacks Share
Operator overloading is capability-based and strict — you implement only the operators you need:
implement Vec3 as Arithmetic {
fn +(self, other: Self): Self { ... }
fn -(self, other: Self): Self { ... }
}
implement Pipeline as Functor {
fn (self, input: []u8): []u8 { ... }
}
let out = pipe(raw_bytes);
extends)extends copies the base type's fields and traits into the new struct. An optional : after the base lists further traits to implement:
struct Dog extends Animal {}
struct T extends Base: Transform, Collision {}
// Traits may also extend capabilities or other traits
trait SafeBuffer extends Trust {
raw fn readByte(self, offset: u64): u8 {}
}
Zith Language Specification — Draft v0.9