Skip to main content
Rust interop lets a Sifr package expose Rust-backed declarations while keeping the same compile-time contracts as ordinary Sifr code. Sifr resolves targets through Cargo metadata, validates bridge-compatible signatures before final build, and reports Rust interop failures as SIFR-RUST-* diagnostics. Rust interop is source-level Cargo integration. It is not Rust ABI loading, dlopen, or a C FFI layer. For library-first walkthroughs, start with blake3 or reqwest.

Package Setup

Declare Rust dependencies in Cargo.toml and Sifr interop policy in sifr.toml:
Cargo.toml
sifr.toml
Use sifr bridge check for Rust interop feedback. It uses the same package check path as sifr check, so decorator parsing, target resolution, bridge contracts, trust policy, and probe diagnostics match the compiler.
sifr repair --check reports drift in Sifr-managed Cargo projection files. sifr repair regenerates only Sifr-owned projection metadata and does not overwrite user-authored bridge files under src/bridges.

Documenting Rejected Syntax

Documentation must distinguish accepted Sifr from deliberately rejected historical syntax structurally. Open a rejected block with exactly ```sifr-rejected. For a stale spelling mentioned inline, place {/* rust-interop-rejected */} on that same physical line in MDX. Markdown files use <!-- rust-interop-rejected --> instead. Headings, surrounding prose, and words such as “no”, “stale”, or “rejected” do not mark an example as rejected. Accepted examples use sifr fences, and Sifr Rust decorators must never appear in python fences.

Compatibility Evidence

Published Rust-interop compatibility rows state how they were verified:
  • compiler-diagnostic observes parser, lowering, metadata, or diagnostic behavior and makes no Cargo build claim.
  • contract-only verifies the named compiler or safety contract, but does not certify a package build or runtime behavior.
  • cargo-probe exercises the real Cargo package graph. Positive directions build generated/package Rust code; negative directions may instead observe a required compiler rejection before Cargo execution.
  • runtime-observed executes the lifecycle or runtime behavior named by the row.
Verification tiers describe the breadth of the subject, not stronger evidence. Tier 1 and tier 3 require cargo-probe; tier 0 requires compiler-diagnostic; tier 2 and tier 4 explicitly name whether each row is contract-only, cargo-probed, or runtime-observed. A contract-only row never satisfies a runtime claim.

Direct Bindings

Use @rust(...) when a public Rust function has a bridge-compatible signature. The target is a dotted path, not a string. Package-authored Rust interop declarations use an ellipsis-only stub body: exactly .... Generated behavior comes from the validated Rust interop metadata.
Direct binding is for compatible Rust signatures. If a crate exposes lifetimes, generics, borrowed returns, trait objects, raw pointers, closures, unsafe fn, or another unsupported shape, write a bridge.

Local Bridges

Local bridges adapt Rust APIs that are not directly bridge-compatible. The Sifr target root bridge resolves to user-authored Rust modules under src/bridges. A local bridge is allowed to be a real adapter boundary: it can convert generated Sifr bridge types into backend Rust types, call one or more Rust functions, convert outputs back to Sifr bridge types, and map Result-typed Rust errors into the public Sifr error type. Use direct @rust(...) bindings only when the Rust signature already matches the Sifr declaration. Use a bridge function for anything that needs input shaping, output shaping, or error normalization.
src/bridges/tokenizer.rs
Package-local bridges may import generated bridge types from crate::__sifr_bridge::<module>. Shared bridge crates must not import those package-specific generated modules; they expose stable Rust types or sifr_runtime::interop helper types instead. Rust functions are not Sifr values. Sifr source cannot pass Rust closures, impl Fn, or Box<dyn Fn> into generated glue.

Async HTTP

Async Rust interop must be declared on an async def. By default, returned futures must be Send; add @rust.async(thread_affinity=tokio_current_thread) only for explicitly current-runtime futures.
Blocking or CPU-heavy Rust calls are never hidden inside async scheduler paths. Declare them explicitly with the existing Sifr blocking or CPU-heavy annotations and call them through the appropriate offload workflow.

Zero-Copy And Views

Zero-copy declarations must make owner, lifetime, mutability, and view metadata explicit. Sifr rejects silent copy fallback.
Returned borrowed views cannot outlive their owner, mutable views require exclusive ownership, and async functions cannot suspend while holding a borrowed Rust view.

Callbacks

Backpressure, overflow, and shutdown behavior must be visible in callback registration declarations. Thread-safety is part of the callback type and the explicit @rust.callback(...) contract.
Sifr rejects callback contracts that permit hidden storage, unmanaged thread entry, missing shutdown behavior, or captures that cannot cross the declared thread boundary.

Diagnostics

Rust interop diagnostics are grouped by contract family: Run sifr --explain SIFR-RUST-CB-0001 for a stable explanation of SIFR-RUST-CB-0001. Rust interop never falls back from zero-copy to copying, never creates a hidden Tokio runtime, and never lets a Rust panic unwind through Sifr user code in recoverable builds.