[HN Gopher] Rust panics under the hood, and implementing them in...
       ___________________________________________________________________
        
       Rust panics under the hood, and implementing them in .NET
        
       Author : todsacerdoti
       Score  : 125 points
       Date   : 2024-09-23 05:26 UTC (17 hours ago)
        
 (HTM) web link (fractalfir.github.io)
 (TXT) w3m dump (fractalfir.github.io)
        
       | CharlieDigital wrote:
       | 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 wrote:
         | .net considers the rust code "unsafe" so you can do unmanaged,
         | non-GC stuff in the Rust portion of the code.
        
           | CharlieDigital wrote:
           | 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?"
        
             | giancarlostoro wrote:
             | 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 wrote:
               | 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.
        
             | jen20 wrote:
             | 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 wrote:
               | 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).
        
             | xelamonster wrote:
             | 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 wrote:
               | 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 wrote:
               | 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 wrote:
               | 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 wrote:
               | > but C# can never take a such change because it would be
               | massively breaking
               | 
               | Out of interest, why?
        
             | riquito wrote:
             | 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.
        
             | tcfhgj wrote:
             | matrix-rust-sdk -> build a Matrix client in the native
             | platform for Windows
        
           | pjmlp wrote:
           | In theory, the compiler could do the same as C++/CLI, and
           | output safe Assemblies when specific code patterns aren't
           | used.
        
             | zamalek wrote:
             | AFAIK, the IJW bits of the CLR are exclusive to Windows.
        
               | neonsunset wrote:
               | And strongly in a "do not use unless you must" category
               | too.
        
               | pjmlp wrote:
               | 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 wrote:
               | Give this tool a try:
               | https://github.com/dotnet/ClangSharp?tab=readme-ov-
               | file#gene...
               | 
               | Just specify the headers to generate bindings for, and
               | then use the generated interop code. Not too different
               | from importing them as it was in the past.
        
               | pjmlp wrote:
               | 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 wrote:
               | 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 wrote:
           | 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.
        
           | asveikau wrote:
           | 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).
        
       | giancarlostoro wrote:
       | 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 wrote:
         | 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.
        
           | neonsunset wrote:
           | 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 wrote:
             | 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 wrote:
             | > 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.
        
           | aninteger wrote:
           | > 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 wrote:
             | F# features creep slowly into C#, though, I feel. (as those
             | of Scala would creep into Java at a delay)
        
         | neonsunset wrote:
         | 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.
        
         | layer8 wrote:
         | 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.
        
           | moomin wrote:
           | Anyone remember J#?
        
             | danielovichdk wrote:
             | IronRuby?
        
             | giancarlostoro wrote:
             | That was a in-between for J++ their Java hard fork.
        
         | aidenn0 wrote:
         | Before Rich made Clojure for the JVM, he wrote dotLisp[1] for
         | the CLR. Not long after Clojure was JVM hosted, it was also CLR
         | hosted[2]. One of my first experiences with ML was F#[3], a ML
         | variant that targets the CLR. These all predate the MIT
         | licensed .net, but prior to that there was mono, which was also
         | MIT licensed.
         | 
         | 1: https://dotlisp.sourceforge.net/dotlisp.htm
         | 
         | 2: https://github.com/clojure/clojure-clr
         | 
         | 3: https://fsharp.org/
        
         | aninteger wrote:
         | 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 wrote:
           | What does Rider use? I assume it's not a proprietary MS one?
           | Or did they license it?
        
             | neonsunset wrote:
             | 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 wrote:
               | > 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 wrote:
               | 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 wrote:
               | 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 wrote:
               | 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 wrote:
         | I remember when Delphi could briefly compile to .NET.
        
         | bj-rn wrote:
         | Idk if it counts but there is VL, a node based visual
         | programming language for .NET.
         | 
         | https://visualprogramming.net
         | 
         | https://thegraybook.vvvv.org/reference/getting-started/dotne...
        
       | bob1029 wrote:
       | > I also plan to explain the difficulties with Rust code catching
       | arbitrary .NET exceptions
       | 
       | Really looking forward to this one.
        
       | evntdrvn wrote:
       | Another LLVM-to-.NET project: https://ericsink.com/tocs/rust.html
        
       | zie1ony wrote:
       | 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 wrote:
         | > 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 wrote:
           | I was thinking about code to code translation. You are right.
        
       | matharmin wrote:
       | 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 wrote:
         | 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 wrote:
         | >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 wrote:
           | Could you elaborate on why that works, or why it's necessary?
        
             | Arnavion wrote:
             | 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.
        
       | ilyagr wrote:
       | 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 wrote:
         | 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._
        
           | SAI_Peregrinus wrote:
           | 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_.
        
       | wild_pointer wrote:
       | Why does Rust panic under the hood? Brb reading the post to find
       | out!
        
       ___________________________________________________________________
       (page generated 2024-09-23 23:01 UTC)