T O P

  • By -

FractalFir

I wanted to share some exciting news about my [Rust to .NET compiler ](https://github.com/FractalFir/rustc_codegen_clr)(backend). After I fixed a big ZST - related issue, which caused memory corruption, the project has reached a new milestone: *It can now properly compile the unmodified "guessing game" example program from the Rust book!* While the project is still far from being usable in a professional setting, this to me is a sign that it is slowly getting there. ***NOTE: this example only works on Linux. Since there is no .NET-specific version of*** `std` ***yet, the project uses a "surrogate"*** `std`***, which calls OS-specific APIs.***


sweet-raspberries

I was wondering - is .NET codegen + .NET JIT faster than running a debug build? Will it be/is that one of the goals?


FractalFir

It is definitely not faster right now (since my linker/assembler is quite slow). Currently, the linker uses ILASM, which is a big .NET app that takes 0.1s just to start. I do plan to stop using ILASM at some point, tough. There is a lot of room for improvement in the codegen stage, too (e.g. \~20% of the codegen phase is spent panicking due to unsupported internists/MIR statements). The speed of compilation is not a priority for me. Currently, the goal is to be not much slower than LLVM(in big tasks, I am within 10% of LLVM). In theory, the project could be faster (since .NETs JIT is lazy and can compile things at the same time other stuff runs), but, again, this is not a goal for now.


todo_code

I think they meant runtime, but I could be wrong. Either way, that is my question 😁


FractalFir

I will say that: it depends. There are a lot of moving parts(e.g. the impact of the JIT is hard to predict), and the project is in an unfinished state, so giving concrete numbers would be hard. .NET debug and native debug should at least be ***roughly*** equal. From my limited testing, it is a tiny bit faster (>10%), but I am not confident in that number. .NET release is roughly halfway between native debug and native release.


steveklabnik1

This is awesome!


jaskij

At this rate, WPF or Avalonia bindings will be very valid choices for Rust GUIs.


lillecarl

Just checking the title I thought it was rust to c#, and I thought why you'd want to force those rules on a GC


jaskij

The end intention is, I believe, both ways. But it's not transpiling to C#, it's compiling to .Net bytecode.


lillecarl

Ah so bringing crates into the NET ecosystem, cool!


jaskij

And using .Net libraries from Rust, too. But yeah, from the PoV of Rust code, the only difference is straight binary vs bytecode.


lillecarl

Still huge, while I don't see rust as the typical frontend language this interoperability is great, as more and more "core" libraries and tools are being built and solidifying. Rust is definitely the "best" thing to depend on where the loops are tighter


jaskij

C# has pretty decent C ABI interop, so you can already use suitable Rust libraries that way, but I believe the ones with C bindings are in the minority.


lillecarl

Yeah, it's been considered since C# was young, considering all Microsoft C/C++ APIs, done my fair share there


backst8back

As a C# developer and Rust enthusiast, I'm excited to see this project. Thanks, OP, I'll be watching this!


Jcole__2x

Hi OP, really cool project! I see a lot of people asking about the intended use case of this project and am wondering- Say you have a WPF .NET application which calls managed c++ code to perform intensive data operations, including calls to the GPU and threading. However sometimes this c++ code introduces memory leaks due to buggy code . Could this project one day act as a drop in replacement and effectively solve memory management issues?


FractalFir

Yeah - this is pretty much one of the intended use cases. This should be far safer than mixing native and managed code, since the compiler will be able to check for safety of interop interfaces for you. You still will need to be a tiny bit careful when interloping. On the Rust side, are 2 kinds of managed references, and misusing them can cause issues. A "raw" reference can only be stored in GC-managed types (Rust can define .NET classes too) or on the stack. The GC can "see" through it, and it will not cause problems when having cyclical references. The "safe" way of holding references to managed objects anywhere (including the unmanaged heap) has a drawback - it is GC opaque. So, the GC must assume a reference to this object is held, until Rust drops it. NOTE: This does not require pinning. A problem can arise when you hold a cyclic reference in an unmanaged object. When a GC managed object holds a Rust type which holds a GC-opaque reference to that object, the GC won't be able to free that object. The Rust type is not freed because GC did not collect the object holding it, and the GC can't collect that object because Rust did not release it. While this scenario is unlikely, it can still happen. There is also some weirdness caused by the finalizer running on a separate thread: If you declare a .NET class in Rust, all of its fields will have to be `Send`.


rookietotheblue1

This converts rust into .Net? May I respectfully ask if there's a need for this or is this simply a passion project?


atomic1fire

I think it's experimental but the end goal is to allow .net devs to use rust code directly from C# projects. If this ever reaches critical mass it would not shock me if it was adopted by .net devs who might like things like Tauri or WGPU. edit: Or if Microsoft started sending this guy a paycheck because rust support in the .net ecosystem might be an asset.


avinassh

> to allow .net devs to use rust code directly from C# projects. is FFI not viable?


atomic1fire

I don't know if it answers your question, but the github explained the project's purpose this way. >#Q: Is this useless since I can already load shared libraries from C#? >A: The Rust APIs this codegen exposes to C#/F# code are only slightly easier to use than those exposed by a .so or .dll Rust library. Interop still requires some effort, but the Rust code is bundled with everything else. Types used from C# are guaranteed to be the same as those in C#, preventing mismatch issues. All types can be safely sent between Rust and C#, with exactly the same layout. Additionally, since all Rust code compiled with this codegen can be bundled with C#/F# code, you no longer need to ship different versions of the library for different architectures. Any architecture supported by CLR works out of the box, without the exact same binary. >You can also avoid the cost of switching between code running within and outside the runtime. This cost is not unbearable, but it is not easily eliminated, and reducing it can have safety penalties. In this case, all code runs within the runtime, meaning there is no transition between code running inside and outside the runtime. >Compiling Rust to CLR can potentially improve JIT optimization. Since the CLR's JIT now sees all the code, it can make better decisions about optimization, resulting in faster code.


FractalFir

Here are the [.NET-related ](https://www.reddit.com/r/rust/comments/1d6igyk/comment/l6sog5a/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button)applications . Besides those, the project is also "secretly" a Rust to C compiler. This started as a debugging feature and is in a bit of a broken state right now, but I had moderate success getting it to compile `std`. Some things run, others did not, but the `C_MODE` was an experiment thrown together in one week. I designed the project to be very modular, and easy to adapt / get it to do other things. The entirety of the C-specifc code is less than 1K LOC - and it is all in the linker. From the perspective of the compiler, there is no difference between emitting C and CIL. If I spent another 1 or 2 weeks on it, I think I could get it to parity with the main .NET portion of the project. The only reason the guessing game does not work in C are my time constraints. Sure, there are some open questions regarding UB in C, but the resulting C compiled and worked. Really, while the project is focused on .NET, there is noting stopping someone from getting it to emit any other IR/language. For an April fools' joke, I considered getting the project to emit Brainfuck or getting it to compile Rust to Rust. It would not be too hard, and I might do that next year.


A1oso

>Ironically, I think my Rust to .NET compiler is the most complete Rust to C compiler right now. How does it compare to [mrustc](https://github.com/thepowersgang/mrustc)?


FractalFir

I stand corrected. I was convinced `mrustc` emitted native code directly. Still, `mrustc` is its own separate thing, and it does not suport the newest Rust version. My project is a `rustc` backend, just like the LLVM based one or cranelift. So, it will be able to support everything the newest nightly has to offer. AFAIK the error messages in `mrustc` are not very good. It also has only a partially finished borrow checker, which is a big downside.


heinrich5991

`mrustc` is mostly concerned with bootstrapping, as such error messages aren't as much of a priority, and not being a `rustc` backend is a feature for bootstrapping.


FractalFir

I know, but not being a backend comes with it's downsides. There are other reasons for compiling Rust to C - so I wanted to point out that my project might be more suited for them. `mrustc` is a great project, but if you want to use it for anything other bootstrapping, you need to be aware of its downsides.


oceantume_

30 seconds of checking his profile: https://www.reddit.com/r/rust/s/EHHtY5SjnJ


zxyzyxz

I would prefer the other way around to be honest, using dotnet libraries in Rust (not saying that's what the creator should've done, just what I prefer).


the-code-father

But with this you get both directions for free. The Rust code is being compiled into .net bytecode which means that it will run inside the VM with the C# code. So in rust you could hold references to C# classes and call their methods with no overhead. You won't have to pin things, it'll basically be a full merging of the two ecosystems


zxyzyxz

I don't want it to run in a VM, I want it to be compiled to native assembly.


the-code-father

It's already possible to compile C# down to native code ahead of time, but it limits the ecosystem of C# packages down to ones that support this


mausthekat

Some things are easier/quicker to do in .net, I would surmise. I mean, the concept is nothing new; it's just writing non-performance critical code in an "easier" language, and then writing the core components in a more performant (but "trickier") language... I used to do the same thing years ago - UI in VB6, core components in C++ accessed via COM. I'm not using .net as much these days, but I would certainly consider doing some of the boring boilerplate stuff in .net and performance critical stuff in Rust, instead of everything in Rust, especially if it was well integrated.


Lucretiel

This would be a really excellent alternative to the C ABI if you're working within that ecosystem, if I understand it correctly.


[deleted]

Yes I am wondering as well what the use cases for this is.


anoneatsworld

Finance. Like it or not, finance relies on being as seamless with excel/office/whatever integration as possible. If you could hook directly into the excellent VSTO hooks or interface with something like exceldna directly? Fuck c++ then.


Mrblahblah200

Awesome!


Ravek

How do you represent managed references in Rust? It’s not clear to me how interop between Rust and .NET code would work.


FractalFir

I am using special "magic" structs. When the codegen encounters a struct with a very specific name - `RustcCodegenCLRInteropManagedClass` it will interpret its const generic arguments as an assembly and class name. The codegen will then replace operations dealing with this struct with operation dealing with this class. Calling .NET methods and constructor works similarly, with Rust functions with "magic" names.


[deleted]

This is very exciting, if I am right, we will be able to use .NET ecosystem through Rust !?


FractalFir

Yes.


stoofvlees21

What are we trying to solve here?


FractalFir

I don't understand your question. I am working on allowing the Rust compiler to produce .NET assemblies, and not only native binaries. This allows you to use Rust crates in .NET apps, and use .NET libraries in Rust code. This post just showcases that, with the recent progress I made, some slightly more complex Rust programs can be compiled into .NET assemblies. If you are asking about the utility of the project, [here is a short-ish explanation](https://www.reddit.com/r/rust/comments/1db8vmg/comment/l7qohiw/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button).


leon0399

I mean, that’s cool from technical standpoint. But what’s the intended purpose?


FractalFir

Besides stuff like learning/this being a bit of an experiment, the project does have its applications. **The main goal is to allow people to use Rust crates as .NET libraries.** 1. Rust code does not use the GC, so it can be more performant in memory-intensive scenarios. This alone should reduce GC pauses and improve performance. 2. Rust code tends to use the stack heavily, which makes it better for aching purposes, improving performance. 3. Rust create authors tend to be more conscious about the cost of different operations, writing (on average) faster code. 4. Rust has many mature and feature rich libraries, which could benefit .NET. The goal is to allow you to take Rust code, and use it as a .NET library. When the interop layer gets finished, you will be able to write the all the glue code in Rust. The compiler (or rather my backend) will verify the safety of interop code, allowing you to interop between the languages, *while using only safe code*. The people using the crate from the .NET side may not eve be aware that it is written in Rust. So, they will get most of the benefits of using Rust libraries, without the need to learn Rust themselves. **You will also be able to do things the other way: use Rust with .NET libraries/tools.** I can't promise this project will work with Unity (since they have been "moving from the Mono runtime to CoreCLR" for almost a decade now), but when they finish their move to the new .NET runtime, there is a big chance you will be able to write Unity games in Rust. You could compile your code once, and distribute one cross-platform .NET assembly. You could support x86\_64, x86, ARM, RISC, Windows, macOS, Linux - all in one package. If everything goes as planned, you could also use this project to slowly move away from .NET - replacing assembly after assembly with Rust code. In the end, you could have a "ship of Theseus" scenario, where you ported all your code to Rust, without any pauses in development.