NVIDIA's CUDA Rust Brings Compile-Time Kernel Safety via Two Crates
In this article
NVIDIA's NVlabs has released two open-source Rust crates that make Rust a first-class language for writing GPU kernels: cuda-oxide for the SIMT programming model and cutile-rs for the newer Tile model. Rust code could already orchestrate CUDA kernel launches, but the kernel body itself had to be authored elsewhere. Both crates use Rust's ownership and borrow-checking rules to reject buffer aliasing at compile time — before any GPU work executes — which is the concrete safety advance worth understanding here.
For ML infrastructure engineers writing custom operators or inference kernels, the stakes are direct. The class of bugs these crates block — a kernel reading and writing the same buffer simultaneously — has historically been a runtime problem, invisible until a silent memory corruption surfaces in a production inference run. As software-layer extraction increasingly outpaces hardware acquisition at the systems frontier, compile-time kernel safety is one of the few mechanisms that directly reduces the debugging surface of that layer.
Two Compilation Pipelines, Two Safety Models
The SIMT track, cuda-oxide, is a custom rustc codegen backend. Functions tagged #[kernel] inside a #[cuda_module] block pass through Rust MIR, then to the community Pliron IR framework (where NVIDIA has added GPU dialects), then to LLVM IR, and finally to PTX. The host binary embeds that device bundle and launches it through a checked contract.
The Tile track, cutile-rs, works differently: a #[cutile::module] proc macro captures the kernel AST and embeds it directly in the host binary, deferring actual compilation to a JIT step through CUDA Tile IR on first launch. The resulting GPU cubin is cached per tile-width specialisation — a new value of the const B triggers a new build.
The safety argument differs between tracks. In cuda-oxide, the output buffer is typed as DisjointSlice rather than &mut [f32]. Because a plain mutable slice would require every thread to hold the same mutable borrow simultaneously — which Rust refuses — the type system forces the exclusive-access invariant. c.get_mut(idx) returns Option, converting out-of-bounds access into a handled branch. A #[launch_contract] attribute declares the block shape, and the generated prepare_vecadd method validates the launch configuration before the safe launch proceeds. A write/read alias on the same buffer produces error[E0502]: cannot borrow c_dev as mutable because it is also borrowed as immutable.
On the Tile side, cutile-rs's .partition([128]) call does three things simultaneously: it gives each tile exclusive ownership of its 128-element chunk, fixes the grid at 1,024 ÷ 128 = 8 tiles, and supplies the const tile width B. The generated launcher takes ownership of all tensors and returns them when the GPU finishes. Nothing executes until .sync_on(&stream). An aliasing attempt produces error[E0382]: use of moved value: z, because the launcher consumed ownership of z when it was partitioned. NVIDIA describes this ownership-follows-tensor-across-launch-boundary guarantee as the stronger of the two — Tile exposes no shared memory or thread indexing to misuse. SIMT retains that lower-level control, but shared memory in cuda-oxide currently requires unsafe.
Requirements and Current Deployability
| Requirement | cuda-oxide (SIMT) | cutile-rs (Tile) |
|---|---|---|
| Status | Early alpha | On crates.io; used in Grout and mistral.rs |
| Rust toolchain | Pinned nightly-2026-04-03 | Stable 1.89 or newer |
| CUDA Toolkit | 12.x or newer | 13.3 |
| GPU | Compute capability 8.0+ | Compute capability 8.0+ |
| OS | Linux | Linux |
| Extra dependencies | clang + libclang; optional system LLVM | None |
| Install | cargo +nightly install cargo-oxide (git) | cargo add cutile |
| Compile model | Ahead-of-time to PTX | JIT via CUDA Tile IR |
| Shared memory | Available, requires unsafe today | Managed by compiler |
cutile-rs is the operationally closer option: it installs from crates.io with a single cargo add, runs on stable Rust 1.89+, requires CUDA 13.3, and is already embedded in Hugging Face's Grout inference engine and in mistral.rs. cuda-oxide requires the pinned nightly toolchain, CUDA 12.x or newer, clang with libclang, and a git-sourced install. Its cargo oxide doctor subcommand validates the environment; cargo oxide new scaffolds a vector-addition project with host and device code in a single file. Neither project has been confirmed production-ready by NVIDIA.
Where This Fits
The GPU kernel was the remaining gap in an otherwise Rust-native NVIDIA stack: the Nova Linux driver is written in Rust, NVIDIA Dynamo has a Rust core, and NVTX ships Rust bindings. Planned inter-language interop will prevent Rust kernel authorship from locking developers out of C++ or Python kernel code in the same project. The two tracks map directly to the two models CUDA C++ and Python already support — SIMT for explicit thread and memory control, Tile for higher-level data-parallel description — so no third programming model is introduced.
For teams primarily concerned with safe tensor arithmetic at inference time, cutile-rs on stable Rust is evaluable today. For those who need explicit warp-level control, cuda-oxide demands a locked toolchain but offers a compile-time safety boundary that C++ CUDA never provided.
Frequently asked questions
What Rust toolchain do I need to use cuda-oxide?
cuda-oxide requires a pinned nightly toolchain, specifically nightly-2026-04-03, along with CUDA 12.x or newer and clang with libclang. It is currently early alpha and not confirmed production-ready by NVIDIA.
Does cutile-rs work on stable Rust?
Yes. cutile-rs runs on stable Rust 1.89 or newer and requires CUDA 13.3. It installs with a single `cargo add cutile` and has no dependency on a custom LLVM or nightly toolchain.
Which projects already use cutile-rs in production?
cutile-rs is published on crates.io and is already embedded in Hugging Face's Grout inference engine and in mistral.rs. NVIDIA has not confirmed either crate as production-ready.
What aliasing errors do these crates catch at compile time?
Passing a buffer as both input and output to a SIMT kernel fails with error[E0502]: cannot borrow c_dev as mutable because it is also borrowed as immutable. The equivalent Tile-side alias fails with error[E0382]: use of moved value: z, because the launcher consumed ownership of the tensor when it was partitioned.
How does cutile-rs partition work and what does it fix at launch time?
The `.partition([128])` call gives each tile exclusive ownership of its 128-element chunk, fixes the grid at 1,024 ÷ 128 = 8 tiles, and supplies the const tile width B. Nothing executes until `.sync_on(&stream)` is called; everything preceding it is a lazy description.
Related Reading
NVIDIA's Switchyard Routes LLM Traffic Between OpenAI and Anthropic APIs
NVIDIA open-sources Switchyard, an Apache 2.0 Rust proxy that translates and routes LLM traffic across OpenAI and Anthropic wire formats, including streams.
NVIDIA PAIR Routes Local AI Requests Across Nodes, Cuts Demo Time 2x
NVIDIA's PAIR beta distributes inference across local GPU nodes via Ollama and LM Studio, showing ~2x faster completion in a three-node demo.
Lily's Local Inference Results Depend on the API and MLX Version
Lily supports a narrow text API. Its documented M5 Max measurements separate decode gains from long-context prefill.