Uniqueness types enforce the at-most-one-reference discipline: a value with ^unique
has at most one live reference at any point. Unlike linear types (^linear, which enforce
exactly-once usage), a unique value may be dropped freely -- it just cannot be aliased.
Uniqueness checking is enabled by default; no flag is required.
| Kind | May be dropped | May be duplicated | Aliasing | Summary |
|---|---|---|---|---|
^unique |
Yes | No | No | At most one live reference |
^linear |
No | No | No | Must be used exactly once |
| (none) | Yes | Yes | Yes | No restrictions (default) |
^unique is the aliasing axis: you can drop it, but you cannot create a second
reference to the same value. ^linear is the usage-count axis: you must consume it
exactly once.
ref<T> is modelled as CK_UNIQUE internally -- uniqueness types make this
explicit in the source language.
;; sort! requires exclusive access -- no other reference may exist
(defn sort! [^unique v : (vec int)] : unit
(in-place-quicksort v))
(defn example [] : unit
(let [v (vec-new 10)]
(sort! v) ;; ownership transferred into sort!
(println v))) ;; OK: sort! returned
;; Each step hands off ownership to the next
(defn buf-write [^unique buf : Buffer data : cstr] : ^unique Buffer ...)
(defn pipeline [^unique buf : Buffer] : ^unique Buffer
(-> buf
(buf-write "header")
(buf-write "body")
(buf-write "footer")))
Creating a second binding that refers to the same unique value is a compile-time error.
(defn modify [^unique ^mut x : int] : unit
(set! x (+ x 1)))
(defn bad [] : unit
(let [x 42
y x] ;; alias created
(modify x))) ;; ERROR TUR-E0200: 'x' is not unique (aliased by 'y')
Passing a ^unique value to a function transfers ownership -- the source
binding is consumed, just like a move.
^unique ^mutCombine ^unique with ^mut for exclusive mutable access. This is the ownership-
transfer equivalent of &mut T borrows.
(defn increment! [^unique ^mut n : int] : ^unique int
(set! n (+ n 1))
n)
Rules:
^unique ^mut parameter may mutate the value in place and return a new ^unique T.^unique ^mut reference while any &T or &mut T borrows are live.You do not have to hand-write ^unique on a let-binding whose initializer is
already a uniquely-owned value. The elaborator infers uniqueness from the
initializer's type (CK_UNIQUE -- e.g. a ref<T> factory result or a call that
returns ^unique) and applies the same alias / use-after-consume checks:
(defn make-ref [n : int] : ref
(ref/from-rc (rc/of n)))
(defn example [] : unit
(let [r (make-ref 42)] ;; inferred ^unique -- no annotation needed
(use-once r)
(use-once r))) ;; ERROR TUR-E0201: 'r' already consumed
At an if/match join the uniqueness of the two arms is combined: two unique
arms join to unique, but a unique arm joined with a shared arm downgrades to
shared (a safe weakening -- the result may be aliased through the shared arm).
Re-annotate ^unique on the binding to force the stricter discipline.
stdlib/unique.tur (auto-loaded) captures the recurring linear-resource shapes so you stop open-coding the manual annotations:
| Form | Use |
|---|---|
(with-unique [name init] body...) |
Scope a ^unique binding over a body without writing the annotation |
(consume x f) |
Thread a unique x through a single transforming call (f x) |
(replace old new) |
Displace a unique value, returning the old one (value-semantics mem::replace stand-in) |
;; Instead of hand-rolling (let [^unique buf (make-buf)] ...):
(with-unique [buf (make-buf)]
(consume buf buf-finish)) ;; ownership threaded into buf-finish
rc<T>Wrapping a ^unique value inside rc<T> is forbidden -- shared reference counting
would introduce aliases:
(rc/new my-unique-val) ;; ERROR TUR-E0202: cannot wrap unique value in rc<T>
| Code | Meaning |
|---|---|
TUR-E0200 |
Value is not unique -- aliased by another binding |
TUR-E0201 |
Cannot copy a unique value |
TUR-E0202 |
Cannot wrap a unique value in rc<T> |
Use tur --explain TUR-E0200 (or any other code) for a detailed explanation and
fix suggestions.
| Feature | Relationship |
|---|---|
ref<T> |
Internally CK_UNIQUE; ^unique makes the constraint explicit |
&mut T |
Temporary unique borrow; ^unique is a permanent ownership transfer |
^linear |
Orthogonal: linear controls usage count; unique controls alias count |
^affine |
^affine restricts duplication but not aliasing |
rc<T> |
Explicitly non-unique -- wrapping a ^unique value is rejected |
^linear, ^affine, ^relevanttur --explain TUR-E0200 -- alias violationtur --explain TUR-E0201 -- copy of unique value