Ownership UX
Rust-level memory safety with far less friction. Nyra infers types, borrows, and Copy behaviour automatically — you only write clone or move when you mean it. Shipped in v2.8–v3.1 (RFCs 0006–0009).
The three defaults
| Mechanism | When | You write |
|---|---|---|
| Borrow | Callee expects &T, you pass owned T | Nothing — compiler inserts & |
| Copy | All fields are Copy (scalars, nested Copy structs) | Nothing — assign freely |
| Clone / Move | Heap duplicate or explicit transfer | clone x, .clone(), or move x |
Type inference (RFC 0006)
fn twice(x) {
return x + x // return type inferred as i32
}
fn id(x){ return x }
fn main() {
let age = 25 // i32
let price = 19.99 // f64
print(id(7)) // T = i32 at call site
print(twice(age))
}fn twice(x: i32) -> i32 {
return x + x // return type inferred as i32
}
fn id(x: i32){ return x }
fn main() -> void {
let age: i32 = 25
let price: f64 = 19.99
print(id(7)) // T = i32 at call site
print(twice(age))
}fn f() { }with noreturn→void- Conflicting
returntypes → error; add-> T - Cannot infer
let→cannot infer type for 'x'; annotate: let x: User = ...
Auto-borrow at calls (RFC 0006)
Before typecheck, the expand pass rewrites call arguments when the callee borrows:
struct User {
name: string
age: i32
}
fn save(u: &User) {
print(u.name)
}
fn main() {
let user = User { name: "Ada", age: 30 }
save(user) // desugared to save(&user)
print(user.name) // OK — user was not moved
}struct User {
name: string
age: i32
}
fn save(u: &User) {
print(u.name)
}
fn main() -> void {
let user: User = User { name: "Ada", age: 30 }
save(user) // desugared to save(&user)
print(user.name) // OK — user was not moved
}| Parameter | Argument | Rewrite |
|---|---|---|
&T | owned T | &arg |
&mut T | mut owned T | &mut arg |
T (owned) | owned T | move (no rewrite) |
Conformance: CONF-COERCE-* in docs/conformance/coercion.md.
Clone (RFC 0006)
let a = "hello"
let b = a.clone() // heap duplicate; a still valid
// All-Copy struct → synthesized Point_clone when neededlet a: string = "hello"
let b = a.clone() // heap duplicate; a still valid
// All-Copy struct → synthesized Point_clone when neededStructs with only Clone fields get a compiler-synthesized .clone(). string.clone() calls str_clone.
move / clone prefixes (RFC 0007)
Unary move and clone at call sites control coercion:
save(move user) // explicit move; skips auto-borrow
save(clone user) // duplicate then pass; user stays valid
move / clone desugar before auto-borrow runs, so they override the default.
Auto-Copy structs (RFC 0008)
fn main() {
let p1 = { x: 1, y: 2 }
let p2 = p1
print(p1.x) // both bindings valid — no annotation needed
print(p2.y)
}fn main() -> void {
let p1 = { x: 1, y: 2 }
let p2 = p1
print(p1.x) // both bindings valid — no annotation needed
print(p2.y)
}Structs with any Move field (e.g. string) stay Move. Optional #[derive(Copy)] documents intent and validates fields — it does not force Copy on unsafe structs.
#[derive(Copy)]
struct Bad { label: string } // ERROR — string is not Copy#[derive(Copy)]
struct Bad { label: string } // ERROR — string is not CopyStruct constructor sugar (RFC 0006)
struct User {
name: string
age: i32
}
struct Point {
x: i32
y: i32
}
fn main() {
let u = User("Ada") // positional → User { name: "Ada", age: 0 }
let origin = Point() // all fields defaulted
print(u.name)
print(origin.x)
}struct User {
name: string
age: i32
}
struct Point {
x: i32
y: i32
}
fn main() -> void {
let u: User = User("Ada") // positional → User { name: "Ada", age: 0 }
let origin: Point = Point() // all fields defaulted
print(u.name)
print(origin.x)
}Missing trailing fields use type defaults (0, 0.0, false, "").
Swift-style diagnostics (RFC 0007)
error: use of moved value 'user'
--> main.ny:8:11
|
6 | save(user)
| ---- value moved here
7 | print(user.name)
| ^^^^ value used after move
|
= note: `save` takes owned `User`; consider `save(&user)` or `save(clone user)`
Errors name the callee, show the function signature, and suggest &, clone, or move. See Diagnostics. Proactive ownership snapshots: nyra inspect.
f64 floating-point (RFC 0009)
let price = 19.99
let lat = 30.0444
print(price)let price: f64 = 19.99
let lat: f64 = 30.0444
print(price)char Unicode scalar (RFC 0010)
let ch = 'A'
let nl = '\n'
if ch == 'A' {
print(ch) // %c — prints the character
}let ch = 'A'
let nl = '\n'
if ch == 'A' {
print(ch) // %c — prints the character
}Single-quoted literals are not lifetimes: 'a' is a char, 'a in &'a T is a lifetime.
Compiler pipeline
Source (.ny) → Lexer → Parser → expand (??, ?., struct ctors, clone/move desugar) → monomorph (+ generic call-site inference) → coerce_auto_borrow → typecheck → ownership (Copy inference, Drop plan) → borrowck (+ callee-aware diagnostics) → lifetimes → Send/Sync → LLVM codegen → opt → clang
88 conformance tests green including CONF-INF-*, CONF-COERCE-*, CONF-DIAG-*, CONF-COPY-*, CONF-F64-*.