matharmin 12 hours ago

As a novice with Rust maintaining a cross-platform library written in Rust, I've found panics/unwinding is one of the most annoying things to figure out in the builds for various platforms.

I'm using nostd and panic=abort to keep the library small (around 200kb after being stripped), but it's really not a well-supported setup.

Depending on the platform, I get an error that the _Unwind_Resume symbol is not defined (despite it not supposed to be needed), or if I define it myself I get a duplicate symbol error on other platforms. I ended up linking to gcc_eh on Linux just to avoid the issue.

I just find it strange that out of all the possible issues with using nostd, unwinding is causing the most issues.

  • agarwaen163 10 hours ago

    I have had the same issue writing rust libraries for things like performant (Gb/s) network log analyzers. Agree this is one of the weak points of the language in my relatively amateur Rust experience. I also faced the rare cargo dependency bug, but still better than alternatives.

  • Arnavion 9 hours ago

    >I get an error that the _Unwind_Resume symbol is not defined (despite it not supposed to be needed)

    Try `-Z build-std=core` if you're not already doing it.

    • an_ko 9 hours ago

      Could you elaborate on why that works, or why it's necessary?

      • Arnavion 9 hours ago

        I know little about it and I'm not sure that it works. It might.

        I do know that for regular std-using programs, std has its own panic strategy that it was compiled with, so even if you set your own code's panic strategy to abort, it still ends up pulling in unwind-related code from libstd. The solution to that is to also recompile libstd with the same parameters as your own code, which is what `-Z build-std` does.

        I don't know if the same thing applies even to libcore, ie for no_std programs. FWIW, coincidentally I spent yesterday making a no_std panic=abort program for a custom freestanding target, and the only missing symbols it needed from me were memcpy and memcmp, even though the code does do panicking things like slice indexing. That program requires `-Z build-std=core` since it's for a custom freestanding target.

CharlieDigital 14 hours ago

From the repo[0]:

   > The project aims to provide a way to easily use Rust libraries in .NET. It comes with a Rust/.NET interop layer, which allows you to easily interact with .NET code from Rust
Are there some standout Rust libraries out there? Very curious about the motivation and use cases.

[0] https://github.com/FractalFir/rustc_codegen_clr

  • jasonjayr 14 hours ago

    .net considers the rust code "unsafe" so you can do unmanaged, non-GC stuff in the Rust portion of the code.

    • CharlieDigital 14 hours ago

      You can already do that from .NET itself.

      My question here is more along the lines of: "what Rust libs are out there that I might be interested in from .NET?"

      • riquito 11 hours ago

        I'm sure that .NET has all libraries you'll need and to absolutely need one from a different language, whatever it is, it's a pretty niche case (e.g. perhaps the extremely good Rust `regex` library is faster, but it would hardly be worth the extra complexity in the general case).

        I think is more common that you have some in-house library in a different language (Rust in this case) and you want to reuse them without rewriting them.

      • jen20 12 hours ago

        One example is the core Temporal.io client, which is implemented in Rust, and wrapped by (most of) the other SDKs including .NET.

        I imagine this pattern will become more common for fat clients where it is desirable have a single implementation with idiomatic language bindings.

        • kodablah 11 hours ago

          Correct, granted it's more complicated than "clients". At least Node.js (Neon), Python (PyO3), and Ruby (rb-sys/magnus) have nice supported bridge wrappers. The .NET-to-Rust interfacing in the Temporal .NET SDK required pure C FFI and P/Invoke and being careful about GC and lifetimes during interop. Can see the bridge at https://github.com/temporalio/sdk-dotnet/tree/main/src/Tempo....

          I can say with regards to panics, .NET is very nice to wrap Rust panics into `SEHException` classes (though of course we strive to be completely panic free).

      • tcfhgj 6 hours ago

        matrix-rust-sdk -> build a Matrix client in the native platform for Windows

      • xelamonster 12 hours ago

        I honestly can't think of any drop-in libraries that would give you much .NET (wow that name sucks to type on mobile) doesn't already give you, though if you're struggling with a particularly slow implementation of something the Rust version is likely faster. I think the more useful case for Rust here is rewriting the heavier or more error-prone parts of your own app logic in it.

        • radicalbyte 10 hours ago

          Rust is a step back with respect to being error prone, unless you're doing manual memory management in .Net (i.e. unsafe).

          The use cases I see are going to be things such as sharing a library over multiple platforms. I've done that with Go in the past via cgo.

          • phs2501 8 hours ago

            Rust is a step sideways if anything. Yeah, you don't have manual memory management headaches in .NET, but you also don't have Rust's fairly strong compile-time guarantees about memory sharing and thread safety.

            Which enables stuff like rayon where you can basically blindly replace map with parallel map and if it compiles, it _should_ be safe to run.

            (I'm not super familiar with the .NET ecosystem, so it's quite possible there's equivalent tooling for enforced thread safety. I haven't heard much about it though, if so.)

            • neonsunset 8 hours ago

              FWIW .NET has many alternatives to popular Rust packages. Features provided by Rayon come out of box - it's your Parallel.For/Each/Async and arr.AsParallel().Select(...), etc. Their cost is going to be different, but I consistently find TPL providing pretty fool-proof underlying heuristics to make sure even and optimal load distrbituion. Rayon is likely going to be cheaper on fine-grained work items, but for coarse grained ones there will be little to no difference.

              I think the main advantages of Rust are its heavier reliance on static dispatch when e.g. writing iterator expressions (underlying type system as of .NET 8 makes them equally possible, a guest language could choose to emit ref struct closures that reference values on stack, but C# can never take a such change because it would be massively breaking, a mention goes to .NET monomorphizing struct generics in the exact same way it happens in Rust), fearless concurrency, access to a large set of tools that already serve C/C++ and confidence that LLVM is going to be much more robust against complex code, and of course deterministic memory reclamation that gives it signature low memory footprint. Rust is systems-programming-first, while C# is systems-programming-strong-second.

              Other than that, C# has good support for features that allow you to write allocation-free or manual memory management reliant code. It also has direct counterpart to Rust's slice - Span<T> which transparently interoperates with both managed and unmananged memory.

              • orf 6 hours ago

                > but C# can never take a such change because it would be massively breaking

                Out of interest, why?

      • giancarlostoro 13 hours ago

        What I think you missed in that statement is that the code is technically still Rust safe, so in theory it wouldn't just be unsafe .NET Code, it would be Rust safe code that happens to run on .NET with a speed up since its less managed. It's kind of genius, if I'm not misunderstanding.

        • bangaladore 9 hours ago

          If you go through the hassle of integrating this, you would be better off manually optimizing your C# code to reduce GC allocations, which you can do within the safe subset of C#. This is not targetted towards increasing performance as it seems the entirety of the rust application gets converted to IL, incurring large performance hits across the board.

          If you do not want JITing, you can already "pre-compile" dotnet code, and you will achieve near-native performance in either case—far better performance than running Rust as IL, as the author indicated.

          In my opinion, the use case for this is minuscule at best.

    • asveikau 9 hours ago

      If I read TFA correctly it looks like they're doing codegen to IL (which is analogous to Java bytecode, but for .net), which would probably mean your rust code is subject to GC?

      It seems like you would get rid of performance benefits of rust.

      But .net can do unsafe pointer operations and it can pin objects to avoid GC (they call it "handles" iirc).

    • pjmlp 13 hours ago

      In theory, the compiler could do the same as C++/CLI, and output safe Assemblies when specific code patterns aren't used.

      • zamalek 13 hours ago

        AFAIK, the IJW bits of the CLR are exclusive to Windows.

        • pjmlp 12 hours ago

          What is exclusive is the Visual C++ backend, because of the usual politics, C++ stuff is not under .NET team.

          Likewise the whole WinRT/UWP, .NET Native and C++/CX, were under WinDev umbrella, not DevDiv.

          Which is why there is this schizophrenic way of how .NET is currently handled on Windows desktop.

        • zamalek 10 hours ago

          I guess another option would be to store the native code as an assembly manifest stream, and cast that to a delegate at runtime. That would achieve much the same thing as IJW without the proprietary bits.

        • neonsunset 13 hours ago

          And strongly in a "do not use unless you must" category too.

          • pjmlp 12 hours ago

            Still easier than dealing with P/Invoke or COM interop for C++ libraries, for C++ skilled devs.

            Pity that never made the cross platform jump.

    • neonsunset 13 hours ago

      The concept of "verifiable code" is effectively dead - even C#'s own syntax is sometimes desugared into what one could consider "unsafe" code in terms of feature use. Unsafe code is safe when used correctly, it does not imply the reliance on undefined or implementation-defined behavior :)

      For all intents and purposes, the non-unsafe subset of Rust is a safe language, and the distinction pretty much does not exist at the IL level.

ilyagr 11 hours ago

I find the relationship between mutlithreading and panics' default behavior confusing.

In a single-threaded program, a panic is not supposed to be "caught" and aborts everything. If the main thread panics, the program stops.

But in a multi-threaded program, a panic terminates only the thread it happened in, and the program is allowed to handle that case without termination.

I'm guessing that setting `panic=abort` changes this behavior, but I'm not sure.

Docs: https://doc.rust-lang.org/std/macro.panic.html#current-imple..., the third sentence of https://doc.rust-lang.org/std/thread/fn.spawn.html

  • zamalek 11 hours ago

    Your assumptions about panic=abort are correct, it will simply terminate the entire process. The single thread and multithreaded behaviors are technically the same. A thread can observe the panic of another, as such, in a single threaded app, which thread would observe the panic in the main thread? There is no other thread to do that.

    What you could do is create a single new thread and pretend as though its the main thread, observing any panics on the original main thread.

    Edit: your mental model should be to avoid thinking about panics (beyond avoiding them in the first place). Panics are supposed to be extremely rare, and typically something that would be difficult to recover from. They are not exceptions; they are not designed to be caught. The difficulty in dealing with them is a feature that prevents anti patterns. If something panics you have a bug.

    • ilyagr 6 hours ago

      From this perspective, the weird part is that mutli-threaded programs can recover from panics in some of the threads (as long as `panic=unwind`). I suppose it's so practically useful that people made an exception for it, without this feature people would have to do inter-process communication (for better or worse).

      • wabain 4 hours ago

        I think what's really special about the main thread is that Rust (and I believe in some cases the OS) forces the process to exit if the main thread completes. I think the difference in panic handling is mostly down to that. I think the description in the docs for std::thread describe this distinction the most explicitly.[1]

        Fundamentally panic recovery works the same way in all threads—for both the main thread and spawned threads the standard library implements panic handling by wrapping the user code in catch_unwind().[2][3] It's more or less possible to override the standard library's behavior for the main thread by wrapping all the code in your main() function in a catch_unwind() and then implementing whatever fallback behavior you want, like waiting for other threads to exit before terminating. In some cases something like this happens automatically, for instance if the main thread spawns other threads using std::thread::scope.[4]

        [1]: https://doc.rust-lang.org/std/thread/#the-threading-model [2]: https://github.com/rust-lang/rust/blob/7042c269c166191cd5d8d... [3]: https://github.com/rust-lang/rust/blob/7042c269c166191cd5d8d... [4]: https://doc.rust-lang.org/std/thread/fn.scope.html

    • SAI_Peregrinus 6 hours ago

      The last paragraph is particularly important!

      Catching panics in Rust is meant pretty much only to avoid unwinding across an FFI boundary, since that would be undefined behavior. Pretty much every other use of `catch_unwind` is a mistake, it's not guaranteed to catch all panics (they can also just abort) so panics are rather different from exceptions. Panics are intended to be useless for control flow, unwinding exists to help debugging, and any panic indicates a bug in the invoking program.

giancarlostoro 14 hours ago

This is really neat, I always wanted to see other languages target .NET much like the JVM was a popular platform to target. Considering .NET is fully MIT licensed I am surprised we dont see more languages that target .NET

  • pjmlp 13 hours ago

    That is why CLR used to mean Common Language Runtime, and there were so many languages on the launch event and .NET SDK bundled on computer magazines back in 2001.

    Then the Windows only focus (for a while there was Rotor), and Microsoft being Microsoft, all that interest faded away and people focused on the JVM, even though MSIL was designed for all kind of languages (well dynamic came later with Iron languages and DLR), including C and C++.

    Nowadays CLR almost feels to have changed into C# Language Runtime, given the C# centric decision on modern .NET design.

    • aninteger 13 hours ago

      > Nowadays CLR almost feels to have changed into C# Language Runtime, given the C# centric decision on modern .NET design.

      Definitely! VB.Net is basically "dead" and F# will never really reach critical mass.

      • jjtheblunt 7 hours ago

        F# features creep slowly into C#, though, I feel. (as those of Scala would creep into Java at a delay)

    • neonsunset 13 hours ago

      While "C# Language Runtime" as a joke term certainly exists, most runtime improvements benefit all languages that target it, individual changes would have different impact on different languages but that's expected. It is likely further devirt and escape analysis work will have greater impact on F# for example.

      • pjmlp 11 hours ago

        As mentioned, I am waiting for .NET 9 final release notes in November.

        Also a solution for code generators, analysers, interceptors usage in .NET libraries and how to consume them from F#, some day, if ever.

      • debugnik 11 hours ago

        > most runtime improvements benefit all languages that target it

        And most CIL ABI additions to the CLR driven by C# totally break them, because the C# ecosystem adopts them immediately (recently even breaking changes in the shared framework!), and there's no modern equivalent to the Common Language Specification.

        Plus, no library writer would care if there was a CLS 2.0 because every CLR language other than C# and F# is in maintenance mode or simply abandoned.

  • layer8 13 hours ago

    Microsoft didn’t manage to have Visual Basic target .NET without turning its semantics into C# with different syntax, so this will be interesting to see.

  • aninteger 12 hours ago

    The lack of a debugger (except for Microsoft's propriety one in VS Code) probably limits the language a bit on open source operating systems.

    • bangaladore 12 hours ago

      What does Rider use? I assume it's not a proprietary MS one? Or did they license it?

      • neonsunset 12 hours ago

        They use their own in-house implementation. There's an OSS alternative from Samsung https://github.com/Samsung/netcoredbg but I haven't heard of anyone using it.

        In general, the debugger is not intentionally made unavailable but rather the "properietary" one is just the original Visual Studio debugger extracted into a standalone package adapted to cross-platform.

        Other than that, CoreCLR exposes rich debugger API, and debugger implementations just integrate with it, there are no "private bits" that would make this task insurmountable, there was just not much need to do so so far, and some Linux folks (as evidenced by skimming through other .NET submissions here) tend to have irrational and unsubstantiated hatred towards .NET which hinders otherwise positive community efforts.

        • jen20 11 hours ago

          > debugger is not intentionally made unavailable

          > rather the "properietary" one

          The fact that it is proprietary intentionally makes it unavailable for use outside of Visual Studio and Xamarin Studio - this actually caused debugging to be unavailable in Rider for a while a few years ago before they built their own.

          • neonsunset 9 hours ago

            This is a strange statement. That said debugger also comes with base C# extension, which is free and debugger aside, MIT, in VS Code on all platforms. Xamarin Studio and VS for Mac are deprecated.

            Given the confidence of your reply, one would assume you'd know this? Unless it's the exact problem I outlined previously, in which case please consider sharing grievances about something you do use actively instead, rather than what you think are .NET's issues.

            • jen20 9 hours ago

              A timeline:

              - The MS Debugger was use in Rider - thus was perfectly functional from a technical perspective.

              - It was later discovered that the license was proprietary, allowed only for MS products. VS Code is one of those. The extension may legally not be used with VS Codium or other such telemetry-neutering builds.

              - The debugger was removed, and debugging of Core CLR apps was unavailable while JetBrains found an alternative (which did not take very long).

              As I alluded to, the fact that this worked, and was just prevented by licensing makes it a construct solely of proprietary software licensing. It was well documented at the time:

              - https://blog.jetbrains.com/dotnet/2017/02/15/rider-eap-17-nu...

              - https://blog.jetbrains.com/dotnet/2017/02/23/rider-eap-18-co... news-about-coreclr-on-windows

              As for daily driving: I was the first person outside of JetBrains to get hands on Rider. The fact that I don't write C# _daily_ in 2024 does not mean I have no first-hand knowledge of what was happening in 2016-2018, or indeed today.

              • neonsunset 9 hours ago

                These events predate .NET Core 3.1, which what I consider the baseline where "the new" .NET gotten good enough for businesses to migrate to. Before that there was a lot of uncertainty, breaking changes and culture shock, the echo of which is still felt to this day. Nonetheless, this holds little influence on the state of affairs in the last few versions, certainly since .NET 5, which, if I understand your first reply correctly, is the criticism in question.

                Would you like to put it against Go for lacking package manager, Java for being stuck on version 8 or Rust for not having stable language server? /s

                Or, to phrase it differently, "this is an issue" - "it was an issue in 2018" - "no, you don't get it, it's a valid criticism because nothing can ever be improved". You see how flawed this argument is?

                I'm so tired of these low effort replies here that it's just sad, in technical conversations in other contexts I'd equally defend another language when someone blatantly misconstrues the facts. I don't have a horse in this race at this point, it's simply annoying to try to converse productively when the quality of replies is this low. I should probably spend time elsewhere.

  • breakingcups 9 hours ago

    I remember when Delphi could briefly compile to .NET.

  • neonsunset 13 hours ago

    Historical precedent and anti-.NET bias - CLI/CIL is a much more powerful and flexible bytecode target than JVM, but it is also not as well-documented with fewer guest languages and, as a result, community know-how. With that said, it really is a breath of fresh air to see projects like this one, alongside ongoing work on F#, the main "other" language of .NET, and a couple small toy-like languages like Draco. There are also IKVM and ClojureCLR/ClojureCLR.Next.

zie1ony 13 hours ago

In the past I have worked on Solidity -> Rust compiler, to enable Solidity on WASM VMs. My pain point was emulation of C3 inheritence in Rust, which I actually was able to implement with a few macros. In Rust -> .NET I'm interested in how he does the `DerefMut` trait.

  • CodesInChaos 12 hours ago

    > how he does the `DerefMut` trait.

    What do you mean by that? A compiler backend isn't even exposed to high level concepts like traits.

    • zie1ony 8 hours ago

      I was thinking about code to code translation. You are right.

wild_pointer 7 hours ago

Why does Rust panic under the hood? Brb reading the post to find out!

bob1029 14 hours ago

> I also plan to explain the difficulties with Rust code catching arbitrary .NET exceptions

Really looking forward to this one.