4 ms·
C# the project that's basically follow up to Microsoft's failed EEE (MS Java Virtual Machine) of Java, is closer to Rust than Java? Bold statement. As Java pro
by Ygg2 2y ago
C# the project that's basically follow up to Microsoft's failed EEE (MS Java Virtual Machine) of Java, is closer to Rust than Java? Bold statement.
As Java programer that used both, adaption to C# was hours, while Rust took months if not weeks.
> Proper generics
Rust and C# generics are not the same. Rust uses monomorphization, C# uses reification, Java uses type erasure.
> Async
The syntax is the same, because Rust copied. The mechanisms aren't, but fine. Point for you.
> Iterator expressions
Other than syntax, not really. I do know Rust iterator are highly optimized as in bound checks are elided, but don't know enough about iterator expressions to make of this point.
> Trait composition
Traits and interfaces have little in common. The sooner you empty your head from such nonsense the better.
As extra points Traits + Generics enable a style of programming that's not achievable in C#. Try implementing interface for type that implements another interface and doesn't implement another.
Oh. Java feature is similar, albeit it uses type erasure for now.
> Extension methods
Rust doesn't have this.
It has impl blocks and implementing traits. For example you can't implement a method for type without owning Trait or Type.
> High order function
Yeah it has those. Same as Java.
> Unsafe block
It's named similarly. But the meaning is completely different. pointer access vs undefined behavior.
> Slice types.
Sure.. Fair point.
So you raise two and a half points.
And you completely omit what makes them grossly different: ADT, monomorphic generic, lack of GC by default, trait solver, mutability xor sharing, lack of nulls...
- neonsunset 2y agoMaybe, but Java programmer would have a lot of issues the moment they go beyond regular LOB application code. Or even when they stay there, the expectation of "it's another Java" would lead to them encountering a lot of surprises. They would also use C# completely ineffectively if they would need to write performance-oriented code. Compared to that, switching from C# to Rust feels like a systems programming focused upgrade, with more than half of the moving parts being familiar the moment you get used to the syntax and understand how they map to C# concepts. And no, C# has generics, like in Rust, while Java has "generics". Generic arguments of class type in .NET result in method body sharing, yes, and represented in the generic signature as __Canon, this is quite close to dispatching by Box<dyn Trait> in Rust. On the other hand, struct generics in C# and struct generics in Rust have identical behavior codegen-wise - they are monomorphized. CoreLib and many other high-performance libraries heavily leverage this fact to make their internal and external abstractions zero-cost. For example System.Numerics.Tensors which is pure C# BLAS package: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.SoftMax.cs https://github.com/dotnet/runtime/blob/main/src/libraries/Sy... combines two operators that are then statically dispatched as it's no different than using traits in Rust save for less convenient syntax. The biggest drawback to .NET generics is that C# type system is not full Hindley-Miller + ability to specify associated types, so the generic signatures can end up being clunky. But this is not an area where work is done and future C# version will see improvements to generic type inference to reduce effort to write zero-cost abstractions and improve simplicity of generic composition. I find the mention of .NET origins as an argument unfortunate, because events that happened more than 20 years ago have no relevance whatsoever to what .NET (the non-Framework one) is today. Async mechanism is closer than you might think as Rust futures are, in a way, state machine step groups, while C# tasks are a more granular and lightweight version of Rust tasks. ValueTasks blur the line slightly due to conditional state machine box allocation. In either case this will lose relevance once runtime handled tasks project is done in one of the future versions which will change the implementation details once more. In general, if you think that low-level and performance-oriented features are comparable with Java, I'd like to ask you to look into this again. Unsafe syntax in Rust allows relaxed memory safety techniques and enables additional syntax, C# is very similar, there are differences, sure, but the latter too exposes full-blown C syntax for pointers, structs with explicit layout and fixed buffers, safe and unsafe variants of stackalloc (Rust lacks it out of box, heh) and more. It's not just pointer syntax. I could also mention FFI and how the amount of boilerplate even with the Panama project is still laughable. Writing interop for either of the two is quite comparable - declare exports/imports and map language-native types to FFI-safe, or pass them as is as if they are already, with the calls being more or less direct, possibly statically linked. Not the case with Java. C# extension methods are Rust associated functions. Also, C# has Kotlin-style nullability, but nullable structs (e.g. int?) are a sugar for Nullable<T> which is similar to Rust's Option<T>, and can be pattern matched (nullable reference types too, but because this was not built into the language at its inception, there is this divide between nullable reference and value types when it comes to type system representation, particularly felt around generics, which is unfortunate).
- Ygg2 2y ago> Maybe, but Java programmer would have a lot of issues the moment they go beyond regular LOB application code. Nope, I wrote a zero copy parser for a game localization. C# is essentially Java with value types, reified generics and some other nifty features (extension methods, stuff like `stackalloc` etc.). > And no, C# has generics, like in Rust, while Java has "generics". While I agree to there is some difference in behaviour, most of the time it's not important. The difference starts with method/argument overloading, and generic type information at runtime. > I find the mention of .NET origins as an argument unfortunate, because events that happened more than 20 years ago have no relevance whatsoever to what .NET (the non-Framework one) is today. If that's the case, the same can be said of this argument. Java is working towards reified generics and value types, the biggest distinctions between C# and Java. Knowing how we got here and why is important. > C# extension methods are Rust associated functions. They aren't. In Rust: Define an implementation of struct outside its owning crate. > Also, C# has Kotlin-style nullability, but nullable structs (e.g. int?) are a sugar for Nullable<T> which is similar to Rust's Option<T> Similar yes, but hey a dolphin and a pig are similar - genetically. In practice, Rust is built around the Option<T>, while Nullable<T> feels like absolute kludge especially with ref vs value types. > In general, if you think that low-level and performance-oriented features are comparable with Java, I'd like to ask you to look into this again. I didn't ever say that. Sure, it has some low-level perf oriented features. Java is working towards adding them (in a way that doesn't break compatibility). You can write performance oriented code in Java, however it's much harder compared to C#. Coding high-perf C# code is similarly like a bit of a different dialect, and not most help from other libs.
- neonsunset 2y agoYou must be trolling and not evaluating either on their current state, or have little understanding of the subject matter as others have pointed out. Please write an efficient text element scanner that uses all AVX512 vector width in Java that matches LLVM codegen and can take any* source of memory? You can easily do that in C#, it's impossible to do it in Java alone. https://github.com/dotnet/runtime/blob/7cd8459e7bd3883f0aa86961c518948630dc2e49/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs#L1566-L1577 https://github.com/dotnet/runtime/blob/7cd8459e7bd3883f0aa86... (it uses portable SIMD and monomorphized generics to express negation to deduplicate vectorized paths, just like you would do so in Rust, except it doesn't have portable SIMD in its standard library yet) *C# 'ref' variables also known as byrefs also work as special GC-aware pointers which can target any memory - stack, GC heap or anything else including unmanaged source and GC is able to disambiguate that and handle that in a performant manner during mark and sweep. This way you can pass a slice from Rust, construct a Span<T> from its ptr and length and then transparently interoperate with the rest of CoreLib, you could do so with the pointers already to an extent, but those required object pinning which was undesirable for something that would be used so ubiquitously in the code that 99% of users end up utilizing without realizing so.