Languages & compilers

Rust to .NET

Compile Rust to .NET and compare managed execution with native output.

Recorded result

Explore the example
Project illustration.

Why I’m interested

A compiler backend connects ecosystems at a deeper level than an API wrapper. Translating Rust into .NET brings Rust values and operations into a managed runtime, where libraries and platform services follow a different set of rules. I'm interested in that boundary: what the compiler emits, how the runtime executes it, and where compatibility work belongs.

Progress

My local fork work covers compiler and linker behavior, nightly compatibility, and release checks. The installed SDK compiled a Rust function to a .NET assembly and produced four results equal to native Rust. The recording uses cargo-dotnet 0.0.1 and nightly-2026-06-17. FractalFir supplies the upstream translation backend.

One function on .NET

Follow the function from source to compiler output, then compare its execution results.

Choose an example

The source function

The Rust-to-.NET backend compiles this function into a managed program. The recorded program returns the same four results as native Rust.

Input

#[inline(never)]
fn add_one(x: i32) -> i32 { x + 1 }
fn main() { for x in [-2, 0, 7, 41] { println!("{} -> {}", x, add_one(x)); } }

Result

cargo dotnet run ./fixture --offline

[profile.release]
overflow-checks = true

A generated managed assembly

The backend emitted a PE assembly for .NET 10. The fixture enables overflow checks. Its four inputs stay within the Int32 range.

Input

fn add_one(_1: i32) -> i32 {
    debug x => _1;
    let mut _0: i32;
    let mut _2: (i32, bool);

    bb0: {
        _2 = AddWithOverflow(copy _1, const 1_i32);
        assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", copy _1, const 1_i32) -> [success: bb1, unwind continue];
    }

    bb1: {
        _0 = move (_2.0: i32);
        return;
    }
}

Result

Artifact: portfolia-add-one.dll
Runtime: .NET 10
SDK: cargo-dotnet 0.0.1
Toolchain: nightly-2026-06-17
SHA-256: b99899d5ba09a3792577b9d5a7a0c9ede44499411e6773c49b937b8d4166ff45

Four native reference inputs

The managed program produced these four lines. Its output matches the native Rust reference byte for byte.

Cases
4
Reference comparison
Equal

Input

-2 -> -1
0 -> 1
7 -> 8
41 -> 42

Result

-2 -> -1
0 -> 1
7 -> 8
41 -> 42

The recorded run uses cargo-dotnet 0.0.1 and nightly-2026-06-17. It compares four inputs within the Int32 range. The next run builds from the newer source checkout. FractalFir authors rustc_codegen_clr.

What comes next

Repeat the same inputs with a build from the newer source checkout and compare the assembly and execution results.

Implementation and credits

How does the same Rust function behave on native and managed runtimes?

A four-input run through the installed Rust-to-.NET SDK compares managed output with native Rust.

Inspect the Rust source, generated assembly identity, and execution results.

The Rust compiler backend produces the managed assembly. Rust exports the recorded comparison.

FractalFir authors rustc_codegen_clr. My local fork covers compiler, linker, compatibility, and acceptance work.

Back to the collection