Nitpick Object Orientation & Traits
Nitpick employs a strict, composition-over-inheritance model. It completely rejects classic class-based inheritance in favor of interfaces (Traits) and isolated data (Structs).
1. Traits and Implementation
A trait defines a set of functions that a
type must implement to satisfy a behavioral contract.
trait:TraitName = { ... };defines the interface.impl:TraitName:for:TypeName = { ... };implements the trait on a specific struct.
trait:Serializable = {
func:to_bytes = buffer();
};
struct:Message = {
int32:id;
};
impl:Serializable:for:Message = {
func:to_bytes = buffer() {
// ... serialize logic ...
pass result;
};
};
2. Dynamic Dispatch
By default, trait implementations are statically resolved
at compile-time (monomorphization) to guarantee performance
and zero-cost abstraction. If runtime polymorphism is
absolutely required, developers must explicitly opt-in using
the dyn keyword to create a “fat pointer” trait
object.
Message:msg = Message{id: 1};
dyn Serializable:obj = msg;
(Note: Because dynamic dispatch obfuscates the
control flow graph, it triggers warnings under strict
nitpick-safety profiles).
3. Data Hiding
All struct fields are pub (public) or
private by default based on the module visibility rules. To
strictly hide internal representations (e.g. for handles or
FFI pointers), use the opaque keyword to define
a type whose internal layout is entirely unknown to the
consumer.
opaque:DatabaseHandle;