The office division is implementing some code in Rust while the same division just posted a success story of their usage of migrating some low latency server code to .NET Core.
Microsoft is a big company and like any other they use JS, TS, Rust, C++, Python, Go and so on. The only thing they do not use is Java ... And I am not 100% confident there.
So let us congratulate Rust for entering the office domain and stop writing swan songs about .NET.
> Java at Microsoft spans from Azure to Minecraft, across SQL Server to Visual Studio Code, LinkedIn and beyond! We use more Java than one can imagine.
C# — and garbage collected languages in general — aren’t going anywhere. With the shift to Arm, languages compiled for a runtime interpreter will get even more important for Microsoft.
But there's all manner of "business systems", e.g. Java/RHEL shops, ".net shops", where GC pauses don't matter, the code is something other than CPU/memory bound anyway,the GC/JIT are mature, and a human writing C++ likely can't do better and will probably do worse. I've seen this with a write-heavy log-ingesting system where the developers used smart pointers everywhere - which internally use atomic reference counting, which implies sync barriers for otherwise entirely unrelated data. I agree, these kinds of places are unlikely to move to Rust - the code writes faster in anything else and runs well. There's also Go, which has a GC but compiles natively, GraalVM, which is AOT compiled Java etc. I definitely agree GC'd languages are not going anywhere.
I'm not sure I agree with C# becoming more important because of ARM, though. I don't think it'll change much - I think shops already invested in certain stacks will mostly stick to them and for all Rust's popularity, it doesn't make sense to implement your CRM in it really. It does, however, make sense to reimplement the C++ parts of the .net runtime and miscellaneous other C and C++ parts of Windows that can benefit from the borrow checker, because it vastly reduces spatial memory safety bugs. There's a massive cost to this, but it's no longer just about about on-prem patching but Azure.
How's that? Apple and its app ecosystem just ship universal binaries, so native, heavily optimized x86_64 and aarch64 code is always ready to go with no added fuss for the user.
You could certainly argue this makes already-large binaries even larger, but users care much more about CPU & RAM efficiency than on-disk binary size. Especially on ARM machines that many people buy for battery efficiency in the first place.
On Linux you don't even bother with universal binaries because you just ship the entire package repository already built for the target CPU. It's only more fuss for the user if they need to pick specific packages from outside their distro.
Has Microsoft still not managed to make something similar work on Windows?
Actually personally I wish we could identify such people & ignore their votes. Actually personally I wish we could take persistent downvoters & fire them into the sun. I don't see or understand why anyone would let contempt so deeply into their soul. The world is no zero sum and I have little patience for those would seem to go out of their way to deny & reject others.
One weird thing about rust is that network sockets and file handles are automatically closed when the handle drops out of scope. The borrow checker takes care of that. If you do the same thing in JavaScript (open a socket then do nothing), the program won’t even quit on its own and you’ll have no idea why.
> One weird thing about rust is that network sockets and file handles are automatically closed when the handle drops out of scope. The borrow checker takes care of that. If you do the same thing in JavaScript (open a socket then do nothing), the program won’t even quit on its own and you’ll have no idea why.
That’s not an inherent problem of GC. You can have linear types or destructors in a GC language.
I worked on an application with extjs that the workers had to close and reopen at least once a day. Or unofficial instructions were portable Firefox.
I think the borrow checker has an unfair reputation of being complicated and magical. Its rules aren't always obvious to a beginner, but at the end of the day it's just type checking / static analysis. People often imagine it also does things at runtime, or affects code semantics in some way. It does not. And it's only scary because because it's so stern :)
EX
using (SomeKindOfHandle handle) {
// handle releases when the scope ends
}
* You can trivially make objects immutable. That feature is tacked on to most GC languages (e.g. Object.freeze) and rarely used, if it's even available. Again mutability of everything encourages bugs.
* You can easily copy values.
* You can use RAII to deterministically and automatically clean up resources, and guard things (e.g. using mutexes).
These features are all in C++ too but Rust lets you have all that and memory safety (and it's better designed than C++).
A classic bug that you wouldn't see in Rust might be passing a list into a function and the function mutating it when you weren't expecting it to. Basically impossible in Rust.
On multiple teams of FAANG engineers, Ive seen the GC be a big part of some meltdown and none of us knew a good way around it. Tuning only goes so far. Otherwise, you just have to give it more memory and CPUs (horizontally or vertically).
Almost always the answer was, this might need to be reworked in C++ entirely or JNI. In same cases it even got funded and was a tremendous success (like 10x fewer resources). However, then you’re dealing with C++ and all of its issues.
I’m looking forward to more value types in Java but managing the heap will always be a bottleneck eventually.
For C# a single executable over a directory of files. For that and Java, a massive runtime, generally slow cold starts, high initial memory use, reduced relative effectiveness for smaller platforms (RPi and similar).
For JavaScript and Python, slow introp, in addition to the negatives above.
It's also possible to leak memory like a sieve in any language.
- .NET had been able to produce single-file (and self-contained when needed) executables way before NativeAOT was introduced
- RPi is a fairly large platform compared to e.g. Arduino (for which Rust should be awesome) and can be well-served by NativeAOT (today). I might still use Rust for that one in the long run but using C# wouldn't be an issue both in terms of resource usage and OS since it's just an arm64 musl flavour of Linux usually with 1GB of RAM or more which is a lot for .NET (for context ~256MiB containers with ASP.NET Core image is a popular target for back-ends)
PTC, Aicas and microEJ are still around, placing Java in hardware that .NET will never be, even with Meadows.
Panama main goal is to replace JNI performance warts, while Java / ART has had mechanisms to do fast interop (@FastNative and @CriticalNative).
Do you have references that say otherwise? (I know you don't, it has JNI level of performance)
p.s.: you have to be insane to use Java today on those listed platforms, which are much better served by C and now Rust. RPi is as small as it gets and on that I'd rather never touch any Java tooling because liking it is literal Stockholm syndrome - it is as low bar as it gets and almost anything else is better.
p.p.s: for anyone's interested, here's the JEP for Panama: https://openjdk.org/jeps/424 do give it a read, then look at C# spans and interop API and ask yourself whether you want to look again at Panama without gouging your eyes out.
I do agree Panama UX could be much better.
On the single executable, my understanding is it sad kind of like a self extracting archive that extracted and ran... Meaning slower start times and the need for write access to the file system... Not to mention the framework still needed to be installed. And only more recently was true single executable possible.
You just give the users an exe or unix binary and it runs.
So what? What does that have to do with rust? There’s no fight between C# and rust where only one language will survive. Both languages are great, and both languages probably have a bright future. C# depends on a lot of low level C/C++ code to function and run fast. I can imagine a future where rust enables c# to get better.
I love rust, but there’s also lots of programs and teams for which c# is a better language choice. So your comment is really weird to me - there’s no reason loving c# should impact your relationship with rust at all.
I think you’re exaggerating things a lot here. The JIT/ILC and the GC are hugely important, complex, core pieces of C#. The performance of the entire .NET ecosystem is rooted in the well written (and well performing) runtime and GC. And there’s not a lot of languages those pieces could be written in. That’s the sort of niche in which rust shines, enabling C# itself to be a great language for applications.
What part of my comment is “technically inaccurate”? I stand by it.
- Metadata representation: type system, reflection
- JIT compiler
- PAL: low-level platform-specific code and interaction with kernel APIs that are mostly consumed by the JIT and GC themselves
- Garbage Collector
- Special features: string interning, threadlocals, ThreadPool, assembly (un)loading and reflection emit, etc.
Only some of the above is written in C++, mostly JIT, GC, and parts of type system facilities.
This contrasted with CoreLib (like C stdlib) which includes code for everything else:
- Primitives (string, int, long, etc., they are partially or fully special-cased by compiler for layout purposes, good example to see how it works - ZeroSharp)
- What you usually see in standard library like APIs for working with strings, file and network IO, math, etc.
CoreLib is written in pure C#.
My issue is with "C++ making C# run fast" which is not the case - the compiler does, and the goal it achieves can be done in most other languages which would only impact the time to JIT the code which would only impact the startup time and not the performance of the compiled C# code.
To give a better example, some features have been historically written in C++ but later on were rewritten in C# for either NativeAOT, which does extra compilation work (also written in C#) or both NativeAOT and regular CLR (you could think of it as the vast majority of CLR being shared but JIT and AOT being two slightly non-overlapping spheres which implement certain runtime features differently, with NativeAOT using C# for what was in C++ previously).
Examples of these are threadlocals/threadstatics and string interning, where rewriting threadlocal storage in C# enabled further optimization in the compiler to fully elide any interop calls to C++ land and completely inline reading of these values, massively improving performance for this case, while rewriting string interning in C# too improved its performance and usability and allowed to remove the legacy cruft that existed to make it work on NativeAOT (still old C++ version for regular CLR but it's a rarely used feature anyway).
Another example is all string searching and manipulation code, all memcpy/memmove routines are too written in C# and always get optimal performance because C# has rich SIMD and intrinsics API which reach optimal HW utilization without ever having to touch C++ because it already gets compiled to comparable asm.
Or ThreadPool, it used to be written in C++ and then has received a rewrite in C# to enable further evolution and performance improvements (like blocked thread detection, or allowing for small methods to be inlined which is not an option with C++).
The overarching trend throughout all versions of .NET after going OSS has been moving more and more C++ code to C#.