Edit source

Rust ownership, lifetimes, and safety

Inferred ordinary ownership

Ordinary TypeScript values require no Rust annotations:

export function append(values: string[], value: string): string[] {
  values.push(value);
  return values;
}

The closed source use graph determines whether Rust can borrow, move, copy, or must own a value. The result is stored in the sealed Rust target program before planning. The planner does not re-infer ownership from emitted syntax.

Explicit native references

Use Rust source types only when an API contract itself distinguishes a borrow:

import type { int32 } from "@tsonic/core/types.js";
import type { Mut, Ref } from "@tsonic/rust/types.js";
import { load, mut, ref, store } from "@tsonic/rust/lang.js";

function increment(value: Mut<int32>): void {
  store(value, load(value) + 1);
}

function read(value: Ref<int32>): int32 {
  return load(value);
}

let value: int32 = 41;
increment(mut(value));
const answer = read(ref(value));

This emits zero-wrapper &mut i32 and &i32 references. ref, mut, load, and store are exact Rust semantic operations, not runtime helper calls.

Authored lifetimes

import type { int32 } from "@tsonic/core/types.js";
import type { Life, Outlives, Ref } from "@tsonic/rust/types.js";

export function choose<
  Short extends Life,
  Long extends Life & Outlives<Short>,
>(left: Ref<int32, Short>, _right: Ref<int32, Long>): Ref<int32, Short> {
  return left;
}

This expresses fn choose<'short, 'long: 'short>(...) -> &'short i32. Static selects 'static; Placeholder selects '_; omitted lifetime arguments use Rust’s legal call-scoped elision. Tsonic never inserts authored lifetime types into ordinary TypeScript.

Safe typed locations

let value: int32 = 1;
const location = addressOf(value);
storePointer(location, loadPointer(location) + 1);

Pointer<T> is a safe, identity-preserving location contract. Rust lowers it to the closed Location<T> runtime carrier, not to *const T or *mut T. Local, parameter, field, and index projections are accepted only when their storage and alias identity are exact.

Native pointers and explicit safety

import { loadNativePointer, unsafeContext } from "@tsonic/core/lang.js";
import type { NativePointer, int32 } from "@tsonic/core/types.js";

export function read(pointer: NativePointer<int32>): int32 {
  return unsafeContext(loadNativePointer(pointer));
}

The operation lowers to native pointer dereference inside an explicit Rust unsafe expression or block. A native-pointer carrier, a requires-unsafe API contract, and an unsafe use site are independent facts. None is inferred from another.