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.
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.
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.)
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.
Out of interest, why?
There have been many projects to improve this like https://github.com/dubiousconst282/DistIL and community libraries that reimplement LINQ with structs and full monomorphization, but the nature of most projects written in C# means their developers usually are not interested or do not need the zero-cost-like abstractions, which limits the adoption, and for C# itself it would need to evolve, and willingly accept a complete copy of existing APIs in LINQ with new semantics, which is considered, and I agree, a bad tradeoff where the simpler cases can be eventually handled through compiler improvements, especially now that escape analysis is back on the menu.
Which is why, in order to "properly" provide Rust-like cost model of abstractions as the first-class citizen, only a new language that targets .NET would be able to do so. Alternatively, F# too has more leeway in what it compiles its inferred types to, but its a small team and as a language F# has different priorities as far as I know.
(And of course the flip side of Rust here is that you need to be able to figure out how to represent your code and data to make it happy, which provides new and interesting limitations to how you can write stuff without delving into "unsafe" territory... something something TANSTAAFL)
Good info though; thanks!
Most of the time - it is, sort of. As in, accessing types that are not meant for concurrent access may lead to logic bugs, but the chance of this violating memory safety is almost nonexistent with the standard library and slim with community ones (for example, such library may use a native dependency which itself is not thread-safe, usually it's clear whether this is the case or not but the risk exists).
The common scenarios are well-known - use Interlocked.Add instead of +=, ConcurrentDictionary<K, V> instead of a plain one, etc. .AsParallel() itself already is able to collect the data in parallel - you just use .ToArray and call it a day.
Other than that, most APIs that are expected to be used concurrently are thread-safe - from the top of my head: HttpClient, Socket, JsonSerializerOptions, Channel<T> and its Reader/Writer can be shared by many threads (unless you specify single reader/writer on construction to reduce synchronization). Task<T> can be awaited multiple times, by multiple threads too. A lot of C# code already assumes concurrent execution, and existing concurrency primitives usually reduce the need for explicit synchronization. Worst case someone just slaps lock (instance) { ... } on it and gets on with their life.
This is to say, Rust provides watertight guarantees in a way C# is simply unable to, and when you are writing low-level code, you are on your own in C# where-as Rust has your back. But in other situations - C# is generally not known to suffer from race conditions and async/await usually allows to flow the data in a linear fashion in multi-tasking code, allowing the underlying implementation to do synchronization for you.
Honestly I'm more interested in code contracts than Rust, as they allow you to make a set of statements of your system which can then be validated statically. I've had very good results using them (and am forever grateful to the colleague who introduced me to them)... and I am interested in Rust (having dabbled with it, only I'm yet to use it in a paying or production project).
So sideways as you said - with C# you can usually rely on the GC for memory management, have fast compile times and what I feel is a more flexible model.. and top tier tooling and a huge and wide range of libraries. Rust can be much more efficient and has much better language-level properties wrt threat safety and a more mature story around native code.
I'd use Rust for system-level code or places where I'd otherwise think of using C++. I keep having discussions with people who want to use Rust for everything - CLI tools, web services (which are doing pumping), business logic and the like. It's getting tiring. The trade-offs are bad. C#, Go and Java are all far better suited & cover pretty much all the niches.
Does Rust have anything to spot resource leaks (i.e. the infamous IDisposable in C#)?