Rust target limitations

The target rejects source semantics that cannot be represented without guessing, open runtime machinery, or a contract not present in checked source or provider evidence.

General boundaries

  • runtime eval, source generation, and arbitrary dynamic member lookup;
  • reflection-based fallback over arbitrary Rust or JavaScript values;
  • unproved ownership, borrow, lifetime, overload, conversion, provider, fallibility, safety, or foundation identity;
  • module cycles whose ESM initialization order cannot be preserved by the closed Rust module-initialization plan;
  • open value graphs whose operations cannot be enumerated statically;
  • rustdoc signatures whose source type, generic, lifetime, const, associated type, ABI, or receiver contract cannot be represented exactly.

Rust-specific boundaries

  • a borrowed result that escapes without one exact source lifetime;
  • overlapping or otherwise unproved mutable borrows;
  • generator, async, closure, or resource storage that captures incompatible authored lifetimes;
  • open generic virtual dispatch whose concrete project instantiations cannot be closed finitely;
  • a trait object or associated-type projection without one exact provider identity and resolved carrier;
  • platform startup, allocator, panic, linker, and target policy inferred from a core or alloc output request.

Generators and retained lifetimes

A generator can retain one authored lifetime or an ordered outlives chain. It cannot collapse unrelated borrows into one storage lifetime:

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

function* alternate<Left extends Life, Right extends Life>(
  left: Ref<string, Left>,
  right: Ref<string, Right>,
) {
  yield left;
  yield right; // rejected when neither lifetime outlives the other
}

The same boundary applies when an async carrier must retain unrelated borrowed results. Tsonic does not claim that either borrow lives as long as the other.

A generator that throws also needs one explicit fallible generator protocol:

function* rows() {
  yield readRow();
  throw new Error("invalid row");
}

Ordinary generators, async generators, next(value), return, throw commands, and yield* are supported when their selected protocol is closed.

Places, conversions, and callable values

Locations over locals, parameters, fields, array elements, and proven disjoint subfields work. An owned-root receiver location remains unsupported.

hashPointer uses canonical location identity. bindPointer retains a closed reference owner, not just its identity token. projectPointer preserves identity, applies the selected conversions, and preserves optionality. Bindings and projections require infallible native callbacks with closed capture lifetimes; their stored callbacks must satisfy 'static.

Raw identity, checked byte offsets, and exact 32/64-bit address integers are supported using a closed raw carrier. They require the alloc foundation; the selected address width must match the executing process. An address integer does not keep its former owner alive or authorize dereference. Layout observations use compile-time descriptors, not runtime provider objects.

Accessor identity is not a native address. Closed scalar layouts support native-backed allocation and initialized block-local or by-value parameter storage. Raw round trips preserve that storage, including pointers held in closed local arrays and data-property objects. Closed local containers can replace entries through simple assignments; analysis must prove every stored pointer’s backing.

Physical record codecs require an explicit complete native-provider field contract and its exact Copy value carrier. Required mutable fields of compiler-owned reference objects can receive retained native storage. Dense native array elements can receive one strided allocation when all bindings, aliases and uses close locally. These refinements use owned Location handles, not Rust references into externally addressed mutable bytes. Replacing an object or array does not retarget an existing pointer.

Escaped arrays, collection methods, optional/accessor fields and open caller boundaries still require additional proofs. Arbitrary conversion callbacks cannot establish a physical view of the same bytes. See native storage boundaries for the exact supported source forms and provider lease obligations.

Ordinary source functions, methods and callbacks retain exact pointer results, including optional results and selected generic parameter transport. This does not infer arbitrary pointer-producing generic bodies or external storage.

A TypeScript assertion cannot manufacture a native conversion:

interface Animal { name: string }
interface Dog extends Animal { breed: string }
declare const animal: Animal;
const dog = animal as Dog; // no proved Rust representation or conversion

Constant tuple indexes and dynamic indexes with an exact Rust integer carrier work. A source-only literal union such as 0 | 1 is not reinterpreted by spelling as a native index.

Direct methods work. Detached structural, static, or open generic method values reject until one exact callable identity, receiver, ABI, and lifetime exist:

const read = counter.read;
const make = Factory.make;

An arrow such as () => counter.read() is valid when creating a new closure has the intended identity and allocation behavior. Tsonic does not make that rewrite automatically because callback comparison and event removal can observe the difference.

Construction and member identity

A Rust value must be fully initialized before an instance method can receive this:

class Example {
  first = 1;
  second = this.read(); // target diagnostic
  read(): number { return this.first; }
}

Complete getter/setter pairs have one storage contract. Setter-only and optional accessor shapes reject when no exact storage representation exists. Static fields require explicit initializers, and static access must retain the exact class identity; constructor aliases and static this are not guessed.

delete is supported for selected JavaScript-array hole semantics. It cannot remove a field from a static Rust record. for...in and switch similarly require an exact own-key or equality policy for the selected carrier.

Native traits, threads, and object graphs

Project inheritance and interface dispatch are supported. A project-authored type cannot yet implement an imported native Rust trait:

import type { Display } from "@tsonic/rust/std/fmt.js";

class Label implements Display {
  // No native `impl Display for Label` contract exists yet.
}

This affects APIs requiring a project type to implement Display, Iterator, Drop, Send, Sync, or a provider trait. External inheritance is limited to the explicitly supported direct JavaScript Error model; transitive external heritage is not general Rust inheritance.

Ordinary project objects and closures use single-threaded ownership. Tsonic does not silently replace them with Arc, a lock, or a sendable closure to satisfy a threaded API. Node workers use their separate structured-clone contract and do not share arbitrary project objects.

Strong Rc graphs are memory-safe but do not reclaim an unreachable strong cycle:

class Link { next?: Link }
const left = new Link();
const right = new Link();
left.next = right;
right.next = left;

No tracing collector is inserted behind ordinary Rust values.

Borrow examples

Returning a borrow requires one exact source lifetime. This is valid only when the authored contract identifies it:

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

function first<L extends Life>(value: Ref<string, L>): Ref<string, L> {
  return value;
}

Two live mutable borrows of overlapping storage are rejected. Tsonic does not clone or box the value merely to satisfy the borrow checker.

Native macros

Rust macros are syntax expansion, not callable metadata. They are not exposed as ordinary provider functions. Put a small native Rust function around a macro when TypeScript must call it, then expose that function through rustdoc.

Provider-authored macro operations with an exact declared row are a separate, supported contract.

ABI and provider shapes

C variadic calls are supported only after the caller supplies the exact promoted tail carriers. Tsonic does not silently apply C default promotions, such as float32 to float64.

The rustdoc provider rejects published signatures that cannot be named by value, including inferred _ types, unstable pattern types, unsupported bound modifiers, and unsized by-value results. Custom receivers, generic associated type projections, associated constraints, opaque captures, raw pointers, function pointers, ABIs, and supported variadics remain available when rustdoc provides a complete contract.

core and alloc applications

Generated core and alloc outputs are libraries. A user-owned native project must provide executable startup, panic behavior, an allocator when required, linker policy, and the target specification. Tsonic will not infer those platform contracts from source code.

JavaScript and Node boundaries

Locale- or timezone-dependent operations require an explicit deterministic data contract; host-default locale/timezone behavior is not compiler semantics. Open object inspection, dynamic field addition that changes a closed Rust layout, arbitrary cyclic graph projection, and Node stream/event schedulers outside the capability’s closed contracts remain rejected.

The JavaScript surface does not currently expose String.raw, wrapper-object construction, or the general object prototype/descriptor/extensibility families. Native Rust strings remain the default; explicit JsString is used only when exact UTF-16 behavior is selected. A neutral UTF-16 char cannot be silently used as a Rust Unicode scalar when it may contain a lone surrogate.

The Rust Node inventory keeps each maintained row implemented, deferred, or hard-rejected. The principal deferred families are worker resource limits, extended and promise-based readline state, and Zstandard operations. Dynamic runtime/debugging families such as vm, repl, inspector, and v8, plus platform contracts without one portable native meaning, remain hard-rejected.

Native compiler boundary

Tsonic generates the exact authored and selected Rust contract. rustc may still reject source that violates native borrow checking, coherence, target, linker, or dependency rules. Tsonic does not rewrite the contract to evade a native diagnostic.

Application entry

Generated binary output requires the entry module to export main with a unit result:

export function main(): void {}

An async entry may return Promise<void>. An unexported function, a function in another module, or a non-unit result is not selected by name recovery.