Microsoft seeks Rust developers to rewrite core C# code
theregister.com
theregister.com
I'm happy to see the increased activity in the space, but searching for a job in Rust is probably still 10x harder than C or C++.
It worked out in the end, and I'm happy to be getting paid to be writing Rust every day, but I hope that the market for Rust jobs continues to grow -- ideally even faster than it has been.
I was _really_ tempted to take a C++ job at some point but I held out.
But not the other way around.
Smart pointers? Just Box, Rc, Arc and Refcel.
Move semantics? It's just another name for ownership.
Sure, the OOP stuff are different, but okay, that in and of itself shouldn't hinder you.
https://en.wiktionary.org/wiki/bikeshedding
If you think the differences between C++ and Rust are "unimportant but easy-to-grasp issues" then you may not know enough about either/both to contribute to the discussion.
We're talking about differences so impactful and well-recognized that everyone from Google[1] to the NSA[2] is advocating for using Rust to reduce the unsafety compared to C++. Are you saying all of them are bogged down in bikeshedding?
[1] https://security.googleblog.com/2022/12/memory-safe-language...
[2] https://www.nsa.gov/Press-Room/News-Highlights/Article/Artic...
But I'm a bit confused by your statement. There's a lot of overlap in the domains that C++ and Rust serve. Isn't it a good thing for job candidates to have an interest in learning about other approaches to their work? To understand what makes C++ better or worse than Rust in difference circumstances?
Why would a mere mention of Rust be a red flag?
This red flag cultural trend is blinding people to nuanced thought.
docker run --name docker-nginx -p 80:80 nginxIt would be a red flag if you were interviewing for react and decided to bring up vue or svelte or angular or whatever else as well.
It’s not like it’s only this C++/Rust type deal that is being picked on. Although I would suggest that rust fans tend to be particularly ardent and loud at the current moment, so interviewers may be far more turned off of you as a person just for bringing it up. Interviewers probably felt this way about people bringing up Python 20 years ago (Python devs of yore were WAY WAY worse about their “HAVE YOU HEARD THE WORD?!” Than rust devs are today), blockchain 7 years ago, etc.
Anyway. As an interviewee, I’d probably try to avoid being the one to bring up alternative technologies.
But nowadays it seems like one has to 'turn on' an interviewer and avoid a minefield of forbidden words to get a job. If a workplace is to become a toxic cult-like environment being policed against such thoughtcrimes as being curious and interested in technology for its own sake I think one would be fortunate to be passed on after an interview to enter such a place.
...why?
Seriously, why on earth? I don't follow this train of thought at all; if they demonstrate proficiency within the scope of the position, why does it matter if they also happen to know other technologies?
"Oh, Alice? Yeah, she was a great candidate, unfortunately she also had experience in Vue, so there's nothing we could do. We decided to hire Bob, who has 3 years less experience with React, but fortunately that's the only stack he's ever heard of."
If anything, it's a sign the person is interested in learning, most great devs I've met were not proficient only with a single technology. This sounds completely alien to me.
Nobody is saying to not expand your knowledge. You’re assuming it of this because it’s literally your only argument, but it’s an unfortunately shitty one, as most logical fallacies tend to be.
Nobody said “don’t have wide experience” but you. What I did say was “I’d probably avoid being an ardent fanboy toward an irrelevant to the interview tech stack”. And that “it’s most often best to leave irrelevant digressions to the interviewer”
Again, you go ahead and give out all the shit tier interview advice you like. For people that actually want jobs, probably try to stick to what’s relevant.
Could I imagine a scenario where it's a non-sequitur? Sure. Not really "don't hire this person" worthy, though.
Rust is hardly a random choice. You can debate its merits for some domains, but replacing C++ in most of the places that C++ is used is exactly what Rust was built for and is widely recognized as doing well at.
> just because
If your engineers are pitching a technology with no more basis than "just because", I can understand dismissing them out of hand. But ask yourself if that's actually what happened, or if they gave specific reasons justifying their proposal and you dismissed them out of hand anyway.
Even if you disagree with the reasons, or agree with them but conclude they are outweighed by other arguments, either of those is totally fine, but dismissing them as "just because" is not an effective way to make technical decisions as a team.
You go ahead an interview however you like, but generally, giving your interviewer a feeling that you plan to shake up their tech stack because you feel like it isn’t going to go well.
And yes, it is very hard.
It seems Rust is in a chicken/egg position: employers avoid it because "there are few developers", developers avoid it because "there are few jobs".
Until recently the majority of Rust jobs were blockchain stuff. Now I see a rise in network infrastructure and security.
You need to know all of the rustisms. Plus you need to have a general knowledge of memory. You need to understand how memory is being abstracted and how to work within those constraints so as not to be slow. You need to know all of the monads, what they mean, why each exists, what the nuance is, and why you might use one over another in a specific situation.
It’s not difficult so much as it just takes so much time and challenging-yourself practice to try to memorize everything.
A “being effective at rust” book would probably dwarf a “being effective at C++” book in pages assuming similar prose. And we all know how much of a beast C++ is.
I've written C++ for close to a decade before switching to Rust, and this is categorically untrue.
> Plus you need to have a general knowledge of memory. You need to understand how memory is being abstracted and how to work within those constraints so as not to be slow.
If anything, this is more true so of C++.
> You need to know all of the monads, what they mean, why each exists, what the nuance is, and why you might use one over another in a specific situation
You can use a lot of stuff before needing to know their full history. You don't need to know the history behind 0, null, pointer, numbers and addresses before, how hardware works, etc to use Option, for example. You also don't need to know the (inconsistent and arcane) history of errno and magic numbers to use Result. Etc, etc.
This vastly over-exaggerates Rust's complexity.
Yes, it borrows concepts from functional programming, but a lot of these concepts are simpler than stuff you deal with in imperative programming.
I think the issues here is that people don't tend to like to change their way of thinking, even if it's for the better. Also, Rust has ample in common with imperative/traditional language, so it's not a huge leap into some alien mental model, as you make it sound.
Further, C++ makes you think way more than Rust; in Rust the compiler does a lot of the thinking for you. Not to mention, the type system is easiest to reason about than C++'s old (and mentally inefficient) template system.
the closest thing to required would be move semantics.
No it doesn't.
people were effective with C++ before std::optional existed, before compiler assisted move semantics existed, and SFINAE is not something anyone has ever needed to understand in order to use something like the STL or any other template based library, such as Boost.
You don't know what you're talking about, this isn't up for debate.
What's the difference?
Arguably becoming an expert in C means you need to understand all the nuances of undefined behaviour. And that’s a much harder process. But it can happen slowly. Rust preloads all the pain. You essentially can’t write rust at all until you understand rust references, lifetimes (implicit and explicit) and the borrowchecker. The payoff is huge, but I found climbing that mountain to be no joke.
And I don’t think a lot of those CVEs come from people misunderstanding the languages. As I understand it they mostly come from honest software bugs. C++ makes it easy for honest mistakes by experts to become security nightmares. This is a controversial opinion - but I think the fault is in C and C++ themselves. Not the programmers who need to work even harder such that they stop ever making mistakes.
I like rust, I like the sensibilities. Maybe my experience is too limited to higher level problems though. There's plenty of room for the likes of Go, Java and C# is all I'm getting at.
But the software which does need or want "native performance" is incredibly important software. I'd use rust for things like databases, operating systems, web browsers, high performance web servers, network services (samba, ssh), and other "systems" software like that.
Most of the lines of code ever written are probably in application code. If you're making a company website, python or C# or whatever is totally fine. But a remarkable proportion of the lines of code your computer actually runs were written in C or C++. Your web server might be in python, but if you use nginx as a proxy - well, thats C++ (I think). Use postgres? C++. Running your software in linux? C. Testing with Chrome? C++. What is python itself written in? I dunno, but I bet its either C or C++. You get the picture.
Whether or not rust is a big deal is largely a matter of focus.
My point is that I just feel like "but someone please think of the ̶ ̶c̶h̶i̶l̶d̶r̶e̶n̶ memory safety" argument is over blown. There are ways to eliminate majority of those issues in cpp as well, but people simply don't care.
If You want to use Rust because it's just better language - go for it, I do it as well. But let's actually use that as an argument, instead of hiding behind superficial ones
If its that easy, why do Google, Apple, Microsoft and basically everyone else keep making memory safety related bugs? Are they all just idiots? Do you think they just don't care about security? Carmack found C++ static analysis tools found mountains of latent bugs in the quake source code - despite the game running great.
Personally I find it a very arrogant statement to claim that memory safety in C++ is easy. If google finds it hard, despite throwing millions each year into the problem, I'm of a mind to believe them.
Yes, people don't care nearly as much as we like to pretend in online debates
But memory bugs in C++ seem genuinely hard even if you do care about the problem. Google and Apple have never (as far as I know) had customer data stolen by some trivial misconfiguration problem. They pay out a lot of money in bug bounties. Google recruits some of the world’s smartest people to look for security problems in their products. And I’m sure they pay a bomb for access to proprietary C++ static analysis tools. And yet, they apparently still can’t consistently write memory safe C++ code. So yeah, I think that writing bug free c++ at scale is hard even if you do care about it.
The same company that has had a stream of no click 0days in imessage, because they parse the messages outside of a sandbox, and patch the issue but not the larger issue of the no-sandbox?
Yeah they don't care about security at all. It's mostly just a thing their marketing department talks about. I'm sure their R&D budget for it is quite limited given their size.
In fact all the laid off people probably feel less smart then average for accepting to work somewhere that treated them like that.
Google chrome has had a fair few CVEs over the years despite having a lot of the worlds best security researchers working full time looking for security vulnerabilities.
I think someone has tried reading the compiler warnings.
There’s an old story about John carmack running a new static analysis tool on the quake3 source code. The code worked well. The tool apparently found a huge list of issues in the code - including a massive pile of real bugs. Then he tried another static analysis tool and it found more real bugs. And so on.
The story is well worth a read. This is a great takeaway:
> This seems to imply that if you have a large enough codebase, any class of error that is syntactically legal probably exists there.
C++ is really hard to do “right”, at any scale in a real team.
http://www.sevangelatos.com/john-carmack-on-static-code-anal...
You can if you're willing to use stuff like .clone() and the interior mutability types. In Rust, you can tell when code has been written to be a bit sloppy because it has that kind of boilerplate. And the compiler checks are a huge help when it comes to refactoring the code and making it cleaner and better-performing.
You might be able to write some simple programs, but I wouldn’t say you know rust yet, or could really be productive with the language.
language itself maybe, but you also need to learn ecosystem, libs, build systems, testing.
Benchmark could be: how fast you can learn and bootstrap some type of app of your choice: high performance DB or torrent server, etc.
> You essentially can’t write rust at all until you understand rust references, lifetimes (implicit and explicit) and the borrowchecker
why is that? You can write rust the same way you write C but with nicer syntax, standard lib and build system and more potential to future expansion.
That’s a really good point that I hadn’t thought of. If we include header files, compiling and linking, makefiles and CMake and the mess of dealing with 3rd party libraries in C - well, yeah. All that stuff is probably worse than learning the rust borrow checker. I think I’ve forgotten how horrific that mountain is for beginners because I learned most of it decades ago, while the pain of learning rust is still fresh.
> why is that? You can write rust the same way you write C
Because I use pointers everywhere in my C code. Rust’s borrow checker simply won’t compile my code if I transliterate it directly from C. There are software patterns which work well in rust with the borrow checker in mind - but they take time to learn and get used to. Until you do, you simply aren’t productive.
borrow checker is more relevant to C references I think.
For pointers you can use Arc or Rc and don't worry about borrow checker.
Disclaimer, I am not a rust or c coder, so my opinion has a high chance to be wrong..
> Disclaimer, I am not a rust or c coder, so my opinion has a high chance to be wrong..
I’ll take a step back. I hope this is helpful to someone and not patronising.
Go and similar languages are memory safe because no matter what go code you write, go runs your program with a GC that makes sure your variables are freed correctly only when it’s safe to do so. Rust’s safety is very different. It comes from checking at compile time that your program is written in a way that obeys a bunch of complex rules. For that to work, you have to write your code very carefully. If you mess up, the compiler doesn’t fix it for you. It just refuses to compile. So you have to write your code with those invariants in mind, or your program won’t build at all.
C code generally doesn’t obey any of rust’s rules - for obvious reasons. If you were to just blithely translate C to rust code, the rust compiler will refuse to compile your program because the rules aren’t being followed. To write rust code that the compiler accepts, you need to first learn the borrow checker’s way of seeing the world. Then you need to restructure your code to work in accordance to rust’s rules. And sometimes that’s a super obscure and tricky problem.
Weirdly, I think it’d be much easier to go the other way. Any compiling rust program could be translated into a memory safe C program if we had a compiler that did that. (I think). And lots of C programmers say learning rust made them better at C - because rust’s rules genuinely teach you to be more disciplined with memory and show you some clear rules for making C code that’s much less error prone.
Not learning the borrow checker is like not learning how objects work in Python. You could write some simple programs. But you’re going to have a bad time, especially if you try to do anything nontrivial, use library code or read anyone else’s programs.
Specially if we are talking about anything related to graphics programming, or asynchronous programming.
I was making a little toy compiler in rust and basically gave up when I wanted to make a change that would have amount to string -> string_view in c++ because in rust I now need to tell the compiler that the String to my &String is going to outlive the struct. Now I understand why it's good but Id rather just let c++ blow my feet off. Maybe once I nail down all the data structures I'll rewrite it in rust.
People used to C or C++ perspective tend to reflexively (over)use references to avoid copying, but references in Rust are to avoid owning, and not-copying is handled in different ways.
BTW, apart from edgiest of edge cases, &String is a useless type in Rust, because String already stores data "by reference" and is never implicitly copied. For loans it's generally better to deref to &str.
Can you give a few examples?
How would not copying be handled?
Sorry for the basic questions. Your comment tickles something in my brain that suggests I need to know.
It is 'borrowing' because you don't own it, not because you don't want to clone/copy it. Sometimes it is cheaper to borrow than it is to clone/copy; sometimes it is not.
I find it confusing still... Been wanting to do a few things that would mean dealing with it streams that could be cp437 or utt8, generally and haven't been quite sure how I want to deal with it.
Basically a BBS door running service, with some extra niceties internally.
Working with the code base gets so annoying if you suddenly want to change something you didn't plan for.
Such changes can have a domino effect through the code.
Now some of the more complex things, have been far more complex to do by hand.. Shared data, channels, Arc, etc. Those have been a bit more cumbersome and still not sure I've done the right thing at times.
Thanks for reaching out!
The times I've seen rust introduced, and tried it my self, it introduces a whole bunch of related tech debt. Like hacks to make builds work
If it's not a good fit, then you're just forcing it, and yeah...
Its like any team that starts writing automation and support projects in python, when their main language is something else. You get trash python scripts and programs. Its another language where technically the domain is right, but the skill is lacking.
I suggest beginning with small, one-off things that don't have much impact. People, even developers, tend to shy away from things that aren't familiar. By introducing Rust in a small, low-risk way, it helps people get familiar with it. They get to build familiarity with building Rust projects, navigating the project structure, and reading docs. I submit pull requests that get people to read Rust code, even if it's just to say "looks good". Their familiarity builds slowly over time, meaning they'll be less triggered by seeing Rust in a larger, more impactful project down the road.
How do you boil a software developer? Slowly.
If they give Rust a chance and your team has a champion to guide them, they'll see its merits. I think a lot of people come to Rust for the performance, but that's not why they stay.
Unfortunately, Rust developers were hard to get by, and we didn't have any internally that could maintain the Rust code at such scale.
The entire back-end ended up being re-written.
In Go, I presume?
In Ethereum (the domain I work in) there are lots of developer tools being written in Rust (Foundry, Build Bear). There is a new Eth node/client being developed in Rust, as well as a "wallet" browser extension. In Bitcoin I know Hiro, a Bitcoin L2, has lots of Rust, but I don't know which parts.
I think Rust has reached to the level where many of us agree on that it's probably a good idea to move on, but probably not many of us have a good idea on how to do that. Hopefully some teams are eager to do that, so we will see some success story soon then more and more teams will explore such possibility.
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.
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.
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.
EX
using (SomeKindOfHandle handle) {
// handle releases when the scope ends
}
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 :)
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.
* 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.
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.
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#.
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.
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.
Makes perfectly sense. Rust is a replacement for ultra performance sensitive parts that might have been written in C/C++ before, but not a replacement for C#.
---------------------------------------------------------------------------------------------------------
Hey there!
I work at Microsoft. I can shed a bit of light here.
We use .NET for TONS of things. Absolutely tons of different products and services. I'm on the Office 365 side of the business, currently managing Deployment for all of the hundreds of services that roll out across the world... And we use .NET extensively.
I'm starting my new position managing a team that does routing next week. They have some things that are extremely performance critical. As others have pointed out, we're talking about supporting services and traffic literally across the planet. When it comes to optimizing, they will find ways to squeeze out what they can.
There are languages like C and C++ that get used for some extreme use cases like I mentioned. Reducing as much overhead as possible in certain situations even leads to .NET apps with unmanaged pieces included with them.
There's been a lot of hype around Rust, and for good reason. But it's a system language. It's not like Microsoft is about to go rewrite millions and millions of lines of code and toss out C# (for anyone getting nervous ). They're just being pragmatic and using an effective tool for the job.
Hope that offers some clarity.
---------------------------------------------------------------------------------------------------------
At the scale of O365 it totally makes sense to look at a high performance non-GC language for your core systems that see the most traffic (I say this as a .Net dev).
Also, at the scale of Microsoft’s core global services, when you are paying for the processing, fast enough (or “CPU efficient enough”) isn't the same as with common apps. Even small efficiency gains are going to yield sufficient savings to be worth a fair amount of developer time.
Could you elaborate? C# has a garbage collector for tracking resources.
Safe rust is safe from data races, but that's just one kind of potential thread safety issue -- one of the simpler kinds. It's not nothing, but not anywhere close to thread-safe.
For example, Rust lifetimes (this is also the case in C++ afaik) can be used to suitably scope the lifetimes of mutexes, to have temporary folders which are deleted when they go out of scope, to require that a connection pool is destroyed _after_ the last connection inside it is returned, etc, etc.
Mostly, garbage collected language do a bad job of cleaning up objects which refer to resources held elsewhere. Java had persistent issues with direct ByteBuffers (which were wrappers around malloc (but not free!)). Locks are easily held too long. File handles are easily left open. And depending on your GC settings, that file descriptor that's holding a 10GB file around may not get cleaned up for hours.
Refcounted languages can be somewhat better, but they don't avoid the bug, they just mitigate the effects.
That's not really true.
Like rust, C# is memory safe, although it comes at it in a very different way.
"safe" rust does inherently prevent a certain kind of race condition in multi-threaded code that C# does not, thought that's more of a nice incremental improvement, not a fundamental one -- i.e. it doesn't make your multi-threaded code thread-safe, but it does prevent a one type of thread safety violation.
Does C# have any kind of GC behaviour guarantees that are similar?
In case you forget the GC will call Dispose but it's entirely up to the GC when that happens, so personally I wouldn't say it's similar.
[1]: https://learn.microsoft.com/en-us/dotnet/fundamentals/runtim...
[2]: https://learn.microsoft.com/en-us/dotnet/csharp/language-ref...
This actually has to be implement in the classes finalizer its not done automatically. The finalizer is called by the GC which if the designer chooses can call Dispose. This is usually done in well designed classes that use unmanaged resources.
Dispose and using give you the determinism if you want it and the finalizer gives you the backstop to prevent leaks if it makes sense.
So it's flexible, but there are footguns about, especially if you're wrapping a handle to something external.
1. https://learn.microsoft.com/en-us/dotnet/api/system.idisposa...
2. https://learn.microsoft.com/en-us/dotnet/csharp/language-ref...
I think you are making a point in the broader discussion of C# vs. rust, but that doesn't fit here. (And, personally, I don't care about that so have nothing to say on the topic.)
I don't know if anyone has really investigated why this is the case but it definitely is.
I believe .net Core has supported several forms of escape analysis for quite a while.
until it isn't. At Microsoft's scale, I imagine performance is actually a major concern for certain pieces of applications.
Despite other comments here, I see nothing in the post that says “we are moving away from C# and rewriting everything in Rust!”
Microsoft is using Rust in Windows as well. They have given examples of how. In that case, they are not “rewriting Windows” but rather targeting specific components that benefit from the characteristics of Rust. Those components are just part of the larger application that is still primarily written in C.
C# easily with C and therefore with Rust. They interoperate well. So it seems likely that MS is following the same plan and rewriting specific components of the larger system that would benefit from Rust.
One example I could think of is if it's currently a SaaS on Azure, but it needs to be portable enough for a version to run in a low end Arm IoT device.
Or if it's deployed to thousands of servers and taking excessive memory or CPU resources for a monitoring agent. Or GC freezes affecting other adjustments systems.
There are plenty of reasons to retire C# to Rust.
I'm also not sold on zig either for many of the same reasons. I prefer my low level languages to be smaller like c. I think that might be true for languages higher level languages too. I just don't like to have to down a lot of documentation on hundreds of different features and concepts behind them.
FWIW I've gotten rust working with the GCC build tools on windows without admin rights, thought it's admittedly somewhat janky.
Edit: ok found https://github.com/sger/RustBooks
Any specific recommendations?
You have to be intentional about some things - largely making sure objects are very short-lived or very long-lived, to avoid long GX pauses. But .NET performs much better than it did 10-15 years ago and I can't think of a fundamental reason why you'd rewrite in Rust.
Work-stealing task schedulers?
It really comes down to performing as much work on a core-local basis and .NET SRV GC does already a lot to avoid inter-core synchronization cost (per-core heaps, I'd expect JVM GCs do a similar, maybe better, job), and so does various thread-safe code in the standard library.
To see how far behind everything is in terms of parallelism and concurrency. It's not even funny how primitive 99.9% of everything out there is in this area.
// Concurrency
using var http = new HttpClient();
var req1 = http.GetStringAsync("https://example.org/");
var req2 = http.GetStringAsync("https://news.ycombinator.com/");
Console.WriteLine(string.Join('\n', await req1, await req2));
// Parallelism
var user = Environment.GetFolderPath(
Environment.SpecialFolder.UserProfile);
var hashes = Directory
.EnumerateFiles(Path.Combine(user, "Downloads"))
.AsParallel()
.Select(path =>
{
using var file = File.OpenRead(path);
return Convert.ToHexString(SHA256.HashData(file));
})
.ToArray();
Console.WriteLine(string.Join('\n', hashes));
For distributed computing, there are Orleans and Akka.net frameworks which allow to achieve it at scale and garden variety of other, simpler frameworks for job scheduling.Does C# have actual green threads or actors? Proper work-stealing schedulers, low-latency guarantees etc.?
If so, that would be cool, Erlang's BEAM VM desperately needs competition.
But I doubt it. From what I am seeing regularly on HN, people prefer to degrade or belittle / minimize the value of the BEAM VM than to admit that the runtime of their favorite language is still not good enough. Cognitive dissonance is getting in the way it seems. Shame.
Some languages intentionally opt for a much more limited way of doing it like Go with goroutines and channels, some have to retrofit an async-alternative to benefit the existing decades of code like Java, and some languages eventually got it right like F#, C#, in a way, Python and JS/TS, and, to dismay of many (and pain I sympathize with), Rust.
Also, in C#, the way to go about it is to just pick the abstraction you think fits the problem best, be it manual threading, async/await Tasks (or anything custom that integrates with it), channels or, as you mentioned, actors for which there are Akka.NET and Orleans. I believe F# is even more flexible in this area.
As for the implementation details of BEAM VM - just look at e.g. the times in 1BRC challenge for BEAM-based submissions - the rift between compiled languages and the former is immense and likely unclose-able, and this trend persists in any (micro)benchmark for BEAM I look at - the overhead of most trivial operations is just too damn high! It was a groundbreaking technology at its inception but today - likely not anymore.
Not sure what* makes you quick to assume that the people working on .NET, JVM and other platforms which care about performance in (massively) parallel domains don't know what they are doing but work-stealing schedulers are yesteryesterday news and "everyone" does them (.NET, Tokio, Go from what I know), both in the form of threadpool implementations and in the form of bespoke parallel abstractions (both examples in my previous comment have these). Same applies to latency-minimization techniques, priority scheduling (worst case you can always have a dedicated worker thread that runs at higher prio, or a scheduler with a pool of those) and more.
(* If you have been burned by Ruby - I did hear about it being subpar in all kinds of ways and observed being very slow but that should rather be an exception than the rule among other languages)
IMO both are really just handy abstractions. The salient question and point is: can we have 10,000+ perceptibly parallel tasks? The BEAM VM does it. And yes we're talking such that are CPU-intensive as well.
> and some languages eventually got it right like F#, C#, in a way, Python and JS/TS
If you say so. I haven't seen any proof of it for my 20+ years of programming. C# is quite the nice language but you are stretching the compliments towards it by ascribing to it that "it got parallelism right". It absolutely did not, as didn't 99% of all languages and runtimes out there. To this day. Having an API for good parallelism doesn't mean your runtime is prepared for it. That's why Orleans and Akka still cannot do what the BEAM VM can do, and likely never will.
And Python and JS? You are just inserting a joke hoping I won't notice here, right? Right? I literally made dozens of thousands of bucks rewriting Python programs where people thought they were oh-so-clever with asyncio et. al., to Golang and to Rust. Easily 200x the throughput, and 99.9% of all parallel bugs disappeared overnight (well, after we launched, I mean). There were a grand total of 7 other bugs remaining we uncovered in the first month in production. I still keep contact with those old colleagues. The app ran unhindered for ~11 months before they finally hired a new team to keep developing it after they let go 10 out of 11 contractors after the project was mostly done (and I was in that group).
> Also, in C#, the way to go about it is to just pick the abstraction you think fits the problem best
Sure, do that, but in the end, and again, what matters most are the building blocks below -- do they enable the true lag-resistant parallelism that doesn't require a lot of fiddling?
> this trend persists in any (micro)benchmark for BEAM I look at - the overhead of most trivial operations is just too damn high! It was a groundbreaking technology at its inception but today - likely not anymore.
What's ground-breaking in terms of academic research matters not to industry, at least 90% of the time.
I work with Elixir (and Golang, and Rust) professionally every day. Even today Phoenix is the most stable web stack I've ever encountered (and one of the very fastest throughout all dynamic languages and no, don't quote TechEmpower; they scarcely know what they're doing, though happily they became more open to PRs gradually which helped their later iterations a lot).
Bombard the BEAM VM, DDoS it, all responses get slightly slower and slower as the load mounts up, but it has to be at its breaking point until you start seeing actually failing request-response pairs. Not to mention parts of your app's supervision tree can fail and they just get restarted without bringing the entire app down (like the database pool).
Try doing that with Ruby. Or Java. Exception after exception, and the server OS process has to be restarted if somebody missed a catch clause (lol).
In truth, Golang and Rust fare quite well too, but that's partly by the virtue of them being able to absorb much more hammering (they are between 20x to 1000x faster than Elixir depending on framework) and not strictly because of their runtimes. They do have good runtimes though. Not as fault-tolerant and lag-minimizing like the BEAM VM, but they are not far.
> Not sure what makes you quick to assume that the people working on .NET, JVM and other platforms which care about performance in (massively) parallel domains don't know what they are doing
1. Sunk cost fallacy;
2. Stockholm syndrome;
3. Risk aversion;
4. Sinclair's law: "It Is Difficult to Get a Man to Understand Something When His Salary Depends Upon His Not Understanding It". You likely command a good salary with C# and have made a good career with it. Of course you will not want to think it's suboptimal.
Shall I go on?
And I am not saying that "they don't know what they are doing". I am saying there is too much inertia that nobody is ever going to make a revolutionary change -- everyone is too afraid and are just swallowing the problems and are becoming experts at avoiding them for as long as possible. Also financial stakeholders will never allow such revolutionary changes, but that's a very different topic.
So yeah, not the same thing.
> work-stealing schedulers are yesteryesterday news
1. I didn't suggest they are revolutionary. I suggested they are a good pattern that's proven, and you also noticed that.
2. What is "yesteryesterday's news" matters not. The ideas of the BEAM VM are quite old and they serve perfectly many businesses today. Are you suggesting trendiness > merit? I hope not.
---
Overall, I will vehemently disagree that you just have to pick a language and it will all work out if you try hard enough. Absolutely not. This forced and imagined equality between languages and runtimes does NOT exist. Some of them are objectively better than others for jobs X and Y and I am tired of people pretending otherwise.
You mentioned 10_000 perceptibly parallel tasks so I threw together a small example - what if we had 10_000_000 concurrently executed tasks instead? This takes 1.2-1.5GB of RAM to run but it can give you a good showcase that .NET ThreadPool can take a lot of punishment and this is far from the worst you could see in one enterprise codebase or another:
var tenMillionTasks = Enumerable
.Range(0, 10_000_000)
.Select(async i =>
{
// Force the yield - this way the methods will continue the execution
// in the form of scheduled continuations (work items), so that we pay
// for context switching too, similar to a more realistic scenario.
await Task.Yield();
for (var j = 10_000; j >= 0; j--)
{
// This is an okay proxy for doing some work since the
// JIT is fairly conservative and won't auto-vectorize this
// because its computation budget is prioritized on more impactful
// optimizations like inlining, CSE, devirtualization, etc.
// (this is completrly compensated by portable SIMD API)
i++;
}
return i;
});
// Scales linearly with the number of CPU cores
var results = await Task.WhenAll(tenMillionTasks);
var average = results.Average();
Console.WriteLine($"Done! The avg is {average}");
You can try it yourself if you're interested. To do so you can get an SDK from https://dot.net/download, execute 'dotnet new console', paste the code to Program.cs and then execute 'dotnet run -c Release'. This is more stressing to the runtime and is likely more fair than another comparison posts a year ago or so here on HN where that evaluated various runtimes by having the task simply wait for a period of time. Nonetheless, you can replicate that too by adding 'var delay = Task.Delay(TimeSpan.FromSeconds(5))' before tasks variable and replacing the lambda passed to .Select with 'async _ => await delay' to see how small the task overhead is.If I am wrong, I'd be happy. It would be about time the big software stacks took proper parallelism to heart.
As far as I understood it, actually Microsoft Exchange and ESENT powers a lot of Office 365, e.g. the compliance tools, the search and e.g. teams chats. Next to it is another pillar: Sharepoint, which is also exposed as OneDrive and is based on SQL server.
Is substrate or has it been part of Exchange?
Compared to C#, it's a low level language, where you have to be concerned with a lot of details.
But then, I'd bet this is for replacing bad C# that does low level tasks, that nobody sane would write but MS pushed for anyway because they were hyping C#.
1. Accept the performance penalty from using a safe garbage collected language (such as their own C#) which is slower which may make the service less attractive and more resource hungry which means more expensive to run at scale.
2. Accept the correctness penalty from C++ which delivers good performance but will cause an endless stream of bugs, some very hard to diagnose.
Rust makes this much easier, because you aren't paying the correctness penalty, your Rust will probably have the same or fewer correctness bugs as your C#, and yet you get the good performance you'd have wanted from C++.
For a 2010 product Rust was not an option, but presumably at some point in the last say five years, Microsoft did the maths and worked out that unless they're killing Office 365 soon or somehow C++ magically becomes safe with no work, a transition to Rust is cost effective.