Skip to content

Ownership Model

DUUMBI implements a Rust-inspired ownership model with borrow checking enforced at the graph validation level — before compilation even begins. Every heap value (string, array, struct) has exactly one owner.

  1. Single owner — every value has exactly one owner at a time
  2. Move semantics — assignment transfers ownership (the original becomes invalid)
  3. Borrowing — you can borrow a reference (&T) without taking ownership
  4. Exclusive mutation — mutable borrows (&mut T) are exclusive (no other borrows allowed)
  5. Scope cleanup — all owned values must be Dropped before scope exit
Alloc ──→ owner is valid
├──→ Borrow ──→ &T exists (read-only, multiple OK)
│ │
│ └──→ borrow ends (reference no longer used)
├──→ BorrowMut ──→ &mut T exists (exclusive, no other borrows)
│ │
│ └──→ borrow ends
├──→ Move ──→ new owner (original invalidated)
└──→ Drop ──→ value deallocated

Once a value is moved, the original variable is invalidated. Any access after Move triggers E021.

[
{ "@type": "duumbi:Alloc", "@id": "…/0",
"duumbi:value": { "@type": "duumbi:ConstString", "value": "hello" } },
{ "@type": "duumbi:Move", "@id": "…/1",
"duumbi:source": { "@id": "…/0" }, "duumbi:target": "new_var" },
{ "@type": "duumbi:Print", "@id": "…/2",
"duumbi:value": { "@id": "…/0" } }
]
❌ E021: use-after-move
"…/0" was moved at "…/1", cannot access at "…/2"

Fix: Borrow instead of move, or access before the move.

You cannot have a &mut T while any other borrow (&T or &mut T) exists. Violating this triggers E022.

[
{ "@type": "duumbi:Alloc", "@id": "…/0",
"duumbi:value": { "@type": "duumbi:ArrayNew", "duumbi:element_type": "i64" } },
{ "@type": "duumbi:Borrow", "@id": "…/1",
"duumbi:source": { "@id": "…/0" } },
{ "@type": "duumbi:BorrowMut", "@id": "…/2",
"duumbi:source": { "@id": "…/0" } }
]
❌ E022: borrow exclusivity violated
Cannot create &mut borrow of "…/0" while &T borrow "…/1" is active

Fix: End the immutable borrow before creating a mutable one (ensure …/1 is no longer referenced).

Moving a value while borrows are active triggers E027.

Alloc ──→ value "s"
├──→ Borrow ──→ &s (still active)
└──→ Move s ──→ ❌ E027: cannot move "s" while "&s" exists

Fix: Drop all borrows before moving the owner.

A reference must not outlive the value it borrows. If the source is dropped while references exist, E026 fires.

Alloc ──→ value "data"
├──→ Borrow ──→ &data
└──→ Drop "data" ──→ ❌ E026: "&data" is now dangling

Fix: Drop references before dropping the source value.

Every owned value must be dropped before scope exit. Missing Drop triggers E024.

[
{ "@type": "duumbi:Alloc", "@id": "…/0",
"duumbi:value": { "@type": "duumbi:ConstString", "value": "hello" } },
{ "@type": "duumbi:Return", "@id": "…/1",
"duumbi:value": { "@type": "duumbi:Const", "value": 0 } }
]
❌ E024: drop incomplete
Owned value "…/0" not dropped before scope exit at "…/1"

Fix: Add { "@type": "duumbi:Drop", "duumbi:value": { "@id": "…/0" } } before the return.

OpDescriptionRuntime cost
AllocAllocate a new value (type-specific)Allocation
MoveTransfer ownershipNo cost (pointer copy)
BorrowCreate &T (immutable reference)No cost (pointer copy)
BorrowMutCreate &mut T (mutable reference)No cost (pointer copy)
DropDeallocate (type-specific duumbi_*_free)Deallocation
  • &T — immutable reference (e.g., &string, &array<i64>)
  • &mut T — mutable reference (e.g., &mut array<i64>)

References are zero-cost at runtime (pointer copies). The borrow checker validates them at graph validation time.

DUUMBI also provides Rust-inspired error handling types that integrate with the ownership model:

TypeDescription
result<T, E>Success (Ok(T)) or failure (Err(E))
option<T>Some value (Some(T)) or nothing (None)

Related ops: ResultOk, ResultErr, ResultIsOk, ResultUnwrap, OptionSome, OptionNone, OptionIsSome, OptionUnwrap, Match.

Error codes E030–E035 enforce exhaustive error handling — unhandled Result or Option values are compile-time errors.

CodeErrorQuick fix
E020Single ownerUse Move or Borrow, not raw copy
E021Use after moveAccess before move, or borrow instead
E022Borrow exclusivityEnd other borrows first
E023Lifetime exceededShorten the reference scope
E024Drop incompleteAdd Drop before scope exit
E025Double freeRemove duplicate Drop
E026Dangling referenceDrop refs before source
E027Move while borrowedDrop borrows before move
E028Lifetime param missingAdd lifetime to function sig
E029Return lifetime mismatchFix return lifetime annotation

See the full Error Codes reference for all codes and examples.