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 00060009).

The three defaults

MechanismWhenYou write
BorrowCallee expects &T, you pass owned TNothing — compiler inserts &
CopyAll fields are Copy (scalars, nested Copy structs)Nothing — assign freely
Clone / MoveHeap duplicate or explicit transferclone 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))
}

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
}
ParameterArgumentRewrite
&Towned T&arg
&mut Tmut owned T&mut arg
T (owned)owned Tmove (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 needed

Structs 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)
}

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

Struct 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)
}

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)

char Unicode scalar (RFC 0010)

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-*.