cd /news/ai-infrastructure/cuda-for-rust-a-practical-guide-to-n… Β· home β€Ί topics β€Ί ai-infrastructure β€Ί article
[ARTICLE Β· art-132344] src=dev.to β†— pub= topic=ai-infrastructure verified=true sentiment=↑ positive

CUDA for Rust: A Practical Guide to Nvidia's Native GPU Programming Support

Nvidia has announced native GPU programming support for Rust through a "two tracks" approach, offering both a low-level CUDA Rust path that maps closely to CUDA C++ semantics and a high-level, iterator-style abstraction for more ergonomic kernel development. The move gives Rust developers first-party tooling and compatibility with new CUDA toolkit releases, replacing reliance on community crates such as rust-cuda, wgpu, and cudarc. A developer notes that Rust does not make kernels faster but reduces host-side footguns, with the borrow checker preventing data races in host code.

by read7 min views2 publishedSep 17, 2026

Originally published at adityarawas.in

Nvidia just gave systems programmers a reason to care about Rust beyond web servers and CLI tools. The announcement of native GPU programming support in Rust β€” dubbed the "two tracks" approach for writing CUDA kernels β€” signals that Nvidia is done treating Rust as a third-party curiosity and is now investing in first-class tooling. If you've ever fought with unsafe blocks in rust-cuda community crates or dealt with brittle FFI bindings to C++ CUDA code, this changes the calculus significantly.

This is a big deal for anyone building GPU-accelerated infrastructure β€” ML training pipelines, data processing engines, or custom inference servers β€” who wants memory safety without giving up raw throughput. Let's break down what actually changed, how it compares to the existing C++/CUDA workflow, and how to get a kernel running today.

Nvidia's "two tracks" strategy refers to two distinct ways developers can now write GPU kernels in Rust:

Track One β€” CUDA Rust (low-level): A near-1:1 mapping to CUDA C++ semantics, giving you direct control over thread blocks, shared memory, warps, and memory coalescing. Think of this as "Rust wearing a CUDA C++ trench coat" β€” same mental model, safer syntax.

Track Two β€” High-level GPU abstractions: A more ergonomic, iterator-style API (similar to rayon for CPU parallelism) that compiles down to efficient kernels without requiring you to manually manage grid/block dimensions for every operation.

This dual approach mirrors how Rust itself handles systems programming: you can drop into unsafe for full control, or stay in safe, ergonomic Rust for 90% of your code. Nvidia is explicitly targeting both the performance-obsessed kernel author and the application developer who just wants GPU acceleration without becoming a CUDA architecture expert.

For the last decade, GPU programming in Rust meant relying on community projects:

rust-cuda (via ptx-builder and nvptx64-nvidia-cuda target)wgpu for cross-platform compute shaderscudarc for safer FFI bindings to the CUDA driver API These worked, but none had Nvidia's official backing, meaning no guaranteed compatibility with new CUDA toolkit releases, no first-party debugging tools (cuda-gdb, nsight), and constant risk of breakage across driver updates. Official support means Rust kernels get the same tooling maturity C++ has enjoyed since CUDA's inception in 2007.

Before jumping into code, it's worth understanding where Rust actually helps and where it doesn't.

Aspect CUDA C++ CUDA Rust
Memory safety Manual, no compiler guarantees Borrow checker prevents data races in host code
Kernel launch syntax <<<blocks, threads>>> macro syntax Explicit function calls with typed launch configs
Build tooling nvcc + Makefiles/CMake cargo +rustc with PTX backend
Error handling Manual cudaError_t checks Result<T, CudaError> with? operator
Package management vcpkg/Conan (fragmented) Cargo (unified, mature ecosystem)
Debugging tools cuda-gdb , Nsight (mature) Nsight support in progress, PTX-level debugging works
Learning curve Steep β€” manual memory management everywhere Moderate β€” safety rails reduce common bugs
FFI interop with C++ CUDA libs Native Requires bindgen orcxx crate
Community crates ecosystem Massive (cuDNN, cuBLAS, Thrust) Growing, some gaps remain
Compile-time kernel verification Limited Stronger β€” type system catches more at compile time

The takeaway: Rust doesn't make your kernels magically faster. The GPU doesn't care what language emitted the PTX. What Rust buys you is fewer footguns on the host side β€” the code managing memory allocation, kernel launches, and data transfer between host and device, which is historically where most CUDA bugs live (use-after-free on device pointers, mismatched grid dimensions, forgotten cudaFree calls).

Here's a minimal walkthrough assuming Nvidia's tooling is installed alongside the standard CUDA Toolkit.

rustup toolchain install nightly
rustup component add rust-src --toolchain nightly

rustup target add nvptx64-nvidia-cuda

nvcc --version
gpu-vector-add/
β”œβ”€β”€ Cargo.toml
β”œβ”€β”€ build.rs
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ main.rs        # host code
β”‚   └── kernel.rs       # device code compiled to PTX
// src/kernel.rs
#![no_std]
#![feature(abi_ptx)]

use core::arch::nvptx;

#[no_mangle]
pub unsafe extern "ptx-kernel" fn vector_add(
    a: *const f32,
    b: *const f32,
    c: *mut f32,
    n: i32,
) {
    let idx = nvptx::_thread_idx_x() + nvptx::_block_idx_x() * nvptx::_block_dim_x();
    if idx < n {
        let i = idx as usize;
        *c.add(i) = *a.add(i) + *b.add(i);
    }
}

This looks almost identical to the equivalent CUDA C++ kernel β€” that's intentional. Track One prioritizes familiarity for engineers porting existing CUDA codebases.

// Equivalent CUDA C++ for comparison
__global__ void vector_add(const float* a, const float* b, float* c, int n) {
    int idx = threadIdx.x + blockIdx.x * blockDim.x;
    if (idx < n) {
        c[idx] = a[idx] + b[idx];
    }
}
php
// src/main.rs
use cust::prelude::*;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    let _ctx = cust::quick_init()?;

    let ptx = include_str!(concat!(env!("OUT_DIR"), "/kernel.ptx"));
    let module = Module::from_ptx(ptx, &[])?;
    let stream = Stream::new(StreamFlags::NON_BLOCKING, None)?;

    let n = 1_000_000;
    let a: Vec<f32> = (0..n).map(|x| x as f32).collect();
    let b: Vec<f32> = (0..n).map(|x| (x * 2) as f32).collect();

    let d_a = a.as_slice().as_dbuf()?;
    let d_b = b.as_slice().as_dbuf()?;
    let mut d_c = DeviceBuffer::<f32>::zeroed(n)?;

    let func = module.get_function("vector_add")?;
    let (grid, block) = (256, 1024);

    unsafe {
        launch!(func<<<grid, block, 0, stream>>>(
            d_a.as_device_ptr(),
            d_b.as_device_ptr(),
            d_c.as_device_ptr(),
            n as i32
        ))?;
    }

    stream.synchronize()?;

    let mut result = vec![0f32; n];
    d_c.copy_to(&mut result)?;

    println!("First 5 results: {:?}", &result[..5]);
    Ok(())
}

Notice the Result propagation with ? β€” every CUDA API call that could fail returns a typed error instead of a raw cudaError_t you have to remember to check. This alone eliminates a huge class of silent failures in production ML pipelines.

For teams that don't need warp-level control, Nvidia's ergonomic API looks closer to this:

use gpu_compute::prelude::*;

fn main() -> GpuResult<()> {
    let device = GpuDevice::default()?;

    let a = device.upload(&vec![1.0f32; 1_000_000])?;
    let b = device.upload(&vec![2.0f32; 1_000_000])?;

    let c = a.zip(b).map(|(x, y)| x + y).collect(&device)?;

    println!("Sum computed on GPU, first value: {}", c[0]);
    Ok(())
}

No grid/block math, no manual PTX compilation step, no explicit stream synchronization. This is the track most application developers will actually use β€” think of it as rayon, but the work executes on the GPU instead of CPU threads.

Cross-compiling to PTX requires a build script to invoke the nightly compiler with the correct target:

// build.rs
use std::process::Command;

fn main() {
    let out_dir = std::env::var("OUT_DIR").unwrap();

    let status = Command::new("cargo")
        .args([
            "+nightly", "rustc",
            "--release",
            "--target", "nvptx64-nvidia-cuda",
            "-p", "kernel",
            "--", "-Z", "build-std=core",
        ])
        .status()
        .expect("failed to build PTX kernel");

    assert!(status.success());
    println!("cargo:rerun-if-changed=src/kernel.rs");
    println!("cargo:rustc-env=OUT_DIR={}", out_dir);
}
[dependencies]
cust = "0.3"

[build-dependencies]

Rust's zero-cost abstractions theoretically hold on GPU targets, but there are real caveats:

nsight-compute before assuming parity.__shared__ semantics via nvptx intrinsics, but the ergonomics are rougher β€” expect more unsafe blocks than idiomatic Rust elsewhere.

ncu --set full ./target/release/gpu-vector-add
nsys profile --stats=true ./target/release/gpu-vector-add

If you're running inference servers or data pipelines in Go or Node.js, the practical integration pattern looks like this:

// Go service calling into a Rust CUDA library via FFI
package main

/*
#cgo LDFLAGS: -L./target/release -lgpu_kernels
#include "gpu_kernels.h"
*/
import "C"
import "fmt"

func main() {
    result := C.run_vector_add()
    fmt.Println("GPU computation triggered from Go:", result)
}

Compile the Rust crate as a cdylib, expose a C ABI with #[no_mangle] extern "C", and you get GPU acceleration in services that otherwise have no business touching CUDA directly. This is the same pattern teams have used for years with C++ CUDA libraries β€” now with a safer implementation underneath.

#![no_std] in kernel codestd types.CUDA_ERROR_INVALID_PTX.__global__-equivalent functions require stream.synchronize() calls will silently read stale device buffers, a bug that's just as easy to introduce in Rust as C++.cust crate plus official Nvidia tooling replaces fragmented community solutions like bindgen FFI bridges.

── more in #ai-infrastructure 4 stories Β· sorted by recency
── more on @nvidia 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/cuda-for-rust-a-prac…] indexed:0 read:7min 2026-09-17 Β· β€”