Unsafe Rust is significantly harder than C, with complex rules that are not fully specified. But luckily you don't have to write a lot of it. And the bit you need can either come from battle-tested libraries or be contained to it's own module where you can rigorously test and fuzz it. And the tooling available for that is pretty good. The article shows good examples of that.
There is some work on making unsafe rust safer, or at least easier to get right. But those are significant undertakings that will take years to pay off.
On the other hand safe rust is so much easier than correct C that I don't particularly mind the tradeoff.
> On the other hand safe rust is so much easier than correct C that I don't particularly mind the tradeoff.
D isn't Rust, but my personal style has gravitated towards the borrow checker rules anyway. It wouldn't be hard at all to do it in C, either.
C's biggest security failing, however, is the lack of array bounds checking. It's always the #1 cause of security bugs in the field, by a wide margin. None of the workarounds in Standard C are attractive. I do not understand why the C committee adds all these bits and pieces of new features, and does not fix that problem. It's not like it's hard:
Safe Rust looks absolutely painful painful compared to other safe languages.
Correct C is not actually hard to write. It can be somewhat hard to convince oneself that it's right.
That's an important difference. Not all C has to be correct. If it's experimental or exploratory code that'll be thrown away. If that just works on one machine or on particular inputs that you care about, or it only certain specific environmental conditions, that can be fine.
Correctness is more than just memory safety and avoiding overflows. They are still bugs in programs and languages that avoid those problems.
When people talk about not needing to write much unsafe Rust, they mean that in any large-scale system, there are probably only a couple hotspots that are complicated enough to need the escape hatch from the Rust safety rules; you write the unsafe code, you give it an interface to the rest of the code, you carefully validate the unsafe code, and you're good to go.
They don't mean that you write unsafe Rust code for experimental or exploratory code that will be thrown away. You could do that, but it's not common.
More importantly: this approach of parsimoniously writing unsafe code in a larger-scale safe system does not work in C/C++. Memory safety issues can (and do) crop up all over your system, not just in the small "unsafe" parts you validate carefully.
Writing unsafe Rust is enough of a pain in the butt (partially thanks to some syntactic salt that comes from the early days that probably should be removed but isn't being dealt with any time soon as far as I know) that it would be harder to prototype in unsafe Rust than safe Rust, IMHO.
That's a valid strategy, but it's harder to write "unsafe-safe" code in C than people think it is. We just did a podcast episode about this with results from the Android team:
Even with careful rules and oversight and secure coding idioms and library exclusions and fuzzing, the rate of memory safety defects in C/C++ code is still pretty high.
Safe Rust takes some time to learn, but once you do, is quite pleasant to write. It is true that it takes some time to get over that hump though. Google found that it was three to six months, but after that, there was no productivity penalty.
Rust offers tons of tools that are very useful for correctness outside of memory safety. I hope to never program in a language without sum types ever again, if possible.
You keep referencing Google's clearly flawed and asymmetric marketing about Rust. It is really unsightly. It has been thoroughly debunked nearly everywhere it was reported but here is a 10 minute recap of some of the clear flaws: https://www.youtube.com/watch?v=vB3ACXSesGo
The summary is that the research that you reference was not done in good faith.
I like prime but just because he says something doesn't mean it's true.
Repeating his claims in text form would be much more useful than linking to a video. I am not anti-video, but for example, I am out in public right now, and cannot watch it, and therefore can't meaningfully respond.
The video is worth a watch before responding. I'll try to summarize but it is unfair to the content. (I specifically opted for it over some others due to its brevity.) In all, the statement from Google lacks context and due diligence.
Some bullets:
* Experience levels in C++ and Rust are not equivalent. The experience gap between C++ and Rust developers can skew results, as C++ has been around much longer, affecting developer proficiency and code complexity.
* Comparison of codebases: legacy C++ vs. greenfield Rust. Comparing a mature C++ codebase with a new Rust project is like comparing apples to oranges; age and complexity can impact productivity metrics significantly.
* Measurement metrics can be misleading. The metrics used to determine productivity often lack depth, failing to account for feature complexity or development duration.
* Organizations have asymmetric composition of resources. The management style and communication within teams can heavily influence productivity, indicating that not all teams function under the same conditions.
* Experience gap between C++ and Rust should give edge to C++ as it has larger pool of candidates to pick from.
* Even in greenfield Rust vs greenfield C++ like Rust drivers in Android, Rust significantly reduced defect rate.
* Measurements in this study were done on teams with same size, rewriting existing service. Neither you or Primagen demonstrate how that would be misleading.
* See first point. While true how can you be sure C++ team isn't overall better than Rust? What if C++ is three senior engineers and Rust is three mediors?
Actual possible problems:
* There isn't yet enough data for language comparison. Too early to tell.
* Observer effect might have influenced the study.
Aha, so you're saying that asymmetric marketing is bad (I agree) and as proof you put up some other asymmetric marketing, namely an influencer`s video... Hard pass :)
Any other source that goes thoroughly over the video of keynote since it was thoroughly debunked nearly everywhere
I could not find it myself.
Also, I would not call it thoroughly.
It sounds like this boils down to one thing: C and Rust have different aliasing rules, with Rust's being much more restrictive.
You can't assume programming in unsafe Rust is like programming in C. Unsafe doesn't mean "do whatever you want"; it means you get to do things that the compiler won't check for you, but it's your responsibility to ensure you're still maintaining Rust's invariants.
So yes, unsafe Rust is harder than C. I think that's fine, though. Most people will be able to get by without writing any unsafe Rust, and those who still need to use it will end up writing very little of it. And tools like MIRI exist to help increase your confidence that your unsafe Rust is correct.
> C and Rust have different aliasing rules, with Rust's being much more restrictive.
Rust is more restrictive by default, but C's restrict keyword lets you opt-in to strict aliasing semantics in exchange for unlocking compiler optimizations that would otherwise be unsound, so it's not like you're free from having to think about aliasing in high performance C code.
It's not so much "more restrictive" as it is "just plain different."
You have behaviors for all four quadrants: there's stuff that's okay in Rust and not okay in C, stuff that's okay in both, stuff that's not okay in both, and stuff that's not okay in Rust and is okay in C.
Coming from a position of extreme Rust ignorance & C++ brain poisoning:
Here’s the issue: waiting_for_elements is a Vec<Waker>. The channel cannot know how many tasks are blocked, so we can’t use a fixed-size array. Using a Vec means we allocate memory every time we queue a waker. And that allocation is taken and released every time we have to wake.
Why isn't a structure that does amortized allocation an option here? I appreciate the design goal was "no allocations in steady-state", but that's what you'd expect if you were using C++'s std::vector: After a while the reserved space for the vector gets "big enough".
One typical approach is double-buffering the allocation but it doesn't work here because you need to pull out the waker list to call `wake()` outside of the mutex. You could try to put the allocation back, but you have to acquire the lock again.
I had an implementation that kept a lock-free waker pool around https://docs.rs/wakerpool/latest/wakerpool/ but now you're paying for atomics too, and it felt like this was all a workaround for a deficiency in the language.
Intrusive lists are the "correct" data structure, so I kept pushing.
I had the same question. I would think that with rust’s Vec, no allocation would occur at steady state. Vec does not automatically resize when removing elements.
Yeah, I'm also not sure what the author is talking about, std::vec also does amortized allocation and never shrinks. There are a bevy of more complicated vector classes in Rust if you need different behaviors, but off the top of my head I don't know why std::vec wouldn't work here.
For me, it really is just so much syntax and mental overhead to track. Somehow it seems like the same goals could have been accomplished with less verbosity.
It almost feels like an engineering tool/language, by devs for devs, instead of by devs for “customers” where customer == “outside” devs, of which I am one.
Great now give me tooling of this millenium (no, I am not going back to vendor everything manually and I am not reinventing every basic data structure I wrote in University in ever project I work on) and we have a deal!
Oh, also get rid of header files, they are archaic. And I want fearless concurrency... And sum types!
If you want those things, you don't want C. Pick a reasonable higher level language you like that makes those decisions for you.
My comment was not to imply that somehow C is superior to X, Y, or Z, but rather to point out that the safety problem with C does have a practical solution.
It cannot possibly. You can catch some low hanging fruit, but asking a compiler to evaluate whether a specific chunk of code is memory safe is basically solving the halting problem.
Can you explain what that low hanging fruit is (or refer me to docs), and also explain it being a decision problem a bit more thoroughly. I will accept that if you have to run a program to decide if it's memory safe then that fits the criteria, but from my understanding static analysis doesn't run the program, and a compiler is parsing and lexing anyway so it should be able to catch at least some things (the low hanging fruit)?
Since I have actually started using C I realized how easy it is to be lazy and not handle memory right so it makes Rust and maybe C++ seem more appealing, but trying to figure out random segfaults it seems like address sanitizer and valgrind catches more than I would have assumed is a low hanging fruit.
I guess I should look more into how Rust manages that safety or understand what memory safety is trying to accomplish more formally. I've taken GC for granted for years until I needed to care about memory.
An example of low hanging fruit is -fwrapv. This flag takes a behavior that is undefined, signed overflow, and converts it to defined behavior, two's compliment wrapping. That improves safety, but it does not prevent all errors. There are many flags like this, but they all tackle individual aspects of the problem, and even if you turn them all on, there are situations which aren't caught.
Thanks. Yeah that makes sense for low hanging fruit. Going through the gcc flags it does seem like a lot of tradeoffs have to be made so you can't cover everything. A quick look through compiling Rust it seems it does at least some of this checking at MIR. I'll have to read more about it.
The compiler really doesn't have the opportunity to simulate every possibility of code. It's not just the matter of whether the function is safe, but every possible use of the function, which the compiler may not see when it is focused on a single compilation unit or a library.
If you want this level of safety, which is possible in C, then you need to use a model checker. Model checking C isn't as trivial as adding a flag to the compiler, but it can be done with about as much overhead as unit testing, if a reasonable idiomatic style is followed, and if the model checker is used well.
It is still a decision problem, and thus has similar limitations, but you can perform steps to ensure that you have some level of soundness with unwinding assertions and other techniques.
Maybe this depends on the application type. I've written a lot of each, and maintaining anything involving concurrency is worlds easier in Rust, since those tend to be the most painful and time-consuming bugs to fix. It's got a learning curve, isn't a particularly easy language, and a big chunk of the "community" sucks, but most of the places I've applied it have been a net hassle savings.
> Using a Vec means we allocate memory every time we queue a waker. And that allocation is taken and released every time we have to wake.
Is that so? On every push back? I’d expect it’d only do an allocation when the current array segment is almost full… as a vector you might write by hand or like the ones in the C++ standard libraries do.
I feel a sort of professional obligation to learn the cryptic unnecessary glyphs and runes that Rust and its ilk proliferate all for this important concept that is memory safety.
However, I would much rather that more languages like Go and Zig simply take off in popularity instead, and that we just reject the eyesore and cognitive mess that is Rust syntax. It’s a language which has no regard for beauty.
For what it's worth, I disagree. I think it would be difficult to design a language more elegant than Rust while accomplishing the same goals. Maybe those goals aren't ones that everyone cares about, and that's OK.
It's a language that's definitely worth learning; it expands the mind in a way that languages like Go (or Java, or any GC language) will not. There is a great beauty and elegance to its design.
Unsafe Rust is significantly harder than C, with complex rules that are not fully specified. But luckily you don't have to write a lot of it. And the bit you need can either come from battle-tested libraries or be contained to it's own module where you can rigorously test and fuzz it. And the tooling available for that is pretty good. The article shows good examples of that.
There is some work on making unsafe rust safer, or at least easier to get right. But those are significant undertakings that will take years to pay off.
On the other hand safe rust is so much easier than correct C that I don't particularly mind the tradeoff.
> On the other hand safe rust is so much easier than correct C that I don't particularly mind the tradeoff.
D isn't Rust, but my personal style has gravitated towards the borrow checker rules anyway. It wouldn't be hard at all to do it in C, either.
C's biggest security failing, however, is the lack of array bounds checking. It's always the #1 cause of security bugs in the field, by a wide margin. None of the workarounds in Standard C are attractive. I do not understand why the C committee adds all these bits and pieces of new features, and does not fix that problem. It's not like it's hard:
https://www.digitalmars.com/articles/C-biggest-mistake.html
The solution has been out there for 15 years now.
The standards people won't even add a standard buffer or slice to the language.
I kinda cry every time I see a new api with foo(void *src, int sz) signatures.
Safe Rust looks absolutely painful painful compared to other safe languages.
Correct C is not actually hard to write. It can be somewhat hard to convince oneself that it's right.
That's an important difference. Not all C has to be correct. If it's experimental or exploratory code that'll be thrown away. If that just works on one machine or on particular inputs that you care about, or it only certain specific environmental conditions, that can be fine.
Correctness is more than just memory safety and avoiding overflows. They are still bugs in programs and languages that avoid those problems.
When people talk about not needing to write much unsafe Rust, they mean that in any large-scale system, there are probably only a couple hotspots that are complicated enough to need the escape hatch from the Rust safety rules; you write the unsafe code, you give it an interface to the rest of the code, you carefully validate the unsafe code, and you're good to go.
They don't mean that you write unsafe Rust code for experimental or exploratory code that will be thrown away. You could do that, but it's not common.
More importantly: this approach of parsimoniously writing unsafe code in a larger-scale safe system does not work in C/C++. Memory safety issues can (and do) crop up all over your system, not just in the small "unsafe" parts you validate carefully.
Writing unsafe Rust is enough of a pain in the butt (partially thanks to some syntactic salt that comes from the early days that probably should be removed but isn't being dealt with any time soon as far as I know) that it would be harder to prototype in unsafe Rust than safe Rust, IMHO.
Write the unsafe code easily in C, and write the rest of it in an easy-to-use language, and you're good to go.
That's a valid strategy, but it's harder to write "unsafe-safe" code in C than people think it is. We just did a podcast episode about this with results from the Android team:
https://securitycryptographywhatever.com/2024/10/15/a-little...
Even with careful rules and oversight and secure coding idioms and library exclusions and fuzzing, the rate of memory safety defects in C/C++ code is still pretty high.
Safe Rust takes some time to learn, but once you do, is quite pleasant to write. It is true that it takes some time to get over that hump though. Google found that it was three to six months, but after that, there was no productivity penalty.
Rust offers tons of tools that are very useful for correctness outside of memory safety. I hope to never program in a language without sum types ever again, if possible.
You keep referencing Google's clearly flawed and asymmetric marketing about Rust. It is really unsightly. It has been thoroughly debunked nearly everywhere it was reported but here is a 10 minute recap of some of the clear flaws: https://www.youtube.com/watch?v=vB3ACXSesGo
The summary is that the research that you reference was not done in good faith.
I like prime but just because he says something doesn't mean it's true.
Repeating his claims in text form would be much more useful than linking to a video. I am not anti-video, but for example, I am out in public right now, and cannot watch it, and therefore can't meaningfully respond.
The video is worth a watch before responding. I'll try to summarize but it is unfair to the content. (I specifically opted for it over some others due to its brevity.) In all, the statement from Google lacks context and due diligence.
Some bullets:
* Experience levels in C++ and Rust are not equivalent. The experience gap between C++ and Rust developers can skew results, as C++ has been around much longer, affecting developer proficiency and code complexity.
* Comparison of codebases: legacy C++ vs. greenfield Rust. Comparing a mature C++ codebase with a new Rust project is like comparing apples to oranges; age and complexity can impact productivity metrics significantly.
* Measurement metrics can be misleading. The metrics used to determine productivity often lack depth, failing to account for feature complexity or development duration.
* Organizations have asymmetric composition of resources. The management style and communication within teams can heavily influence productivity, indicating that not all teams function under the same conditions.
Counter bullets:
* Experience gap between C++ and Rust should give edge to C++ as it has larger pool of candidates to pick from.
* Even in greenfield Rust vs greenfield C++ like Rust drivers in Android, Rust significantly reduced defect rate.
* Measurements in this study were done on teams with same size, rewriting existing service. Neither you or Primagen demonstrate how that would be misleading.
* See first point. While true how can you be sure C++ team isn't overall better than Rust? What if C++ is three senior engineers and Rust is three mediors?
Actual possible problems:
* There isn't yet enough data for language comparison. Too early to tell.
* Observer effect might have influenced the study.
Aha, so you're saying that asymmetric marketing is bad (I agree) and as proof you put up some other asymmetric marketing, namely an influencer`s video... Hard pass :)
Any other source that goes thoroughly over the video of keynote since it was thoroughly debunked nearly everywhere I could not find it myself. Also, I would not call it thoroughly.
> thoroughly debunked
Ok. Looks inside. First warning sign Primagen. Ok. What did he talk about:
> There is lies, damn lies and statistics.
> Go is goated, let's go.
Truly dedunked.
It sounds like this boils down to one thing: C and Rust have different aliasing rules, with Rust's being much more restrictive.
You can't assume programming in unsafe Rust is like programming in C. Unsafe doesn't mean "do whatever you want"; it means you get to do things that the compiler won't check for you, but it's your responsibility to ensure you're still maintaining Rust's invariants.
So yes, unsafe Rust is harder than C. I think that's fine, though. Most people will be able to get by without writing any unsafe Rust, and those who still need to use it will end up writing very little of it. And tools like MIRI exist to help increase your confidence that your unsafe Rust is correct.
> C and Rust have different aliasing rules, with Rust's being much more restrictive.
Rust is more restrictive by default, but C's restrict keyword lets you opt-in to strict aliasing semantics in exchange for unlocking compiler optimizations that would otherwise be unsound, so it's not like you're free from having to think about aliasing in high performance C code.
It's not so much "more restrictive" as it is "just plain different."
You have behaviors for all four quadrants: there's stuff that's okay in Rust and not okay in C, stuff that's okay in both, stuff that's not okay in both, and stuff that's not okay in Rust and is okay in C.
Coming from a position of extreme Rust ignorance & C++ brain poisoning:
Here’s the issue: waiting_for_elements is a Vec<Waker>. The channel cannot know how many tasks are blocked, so we can’t use a fixed-size array. Using a Vec means we allocate memory every time we queue a waker. And that allocation is taken and released every time we have to wake.
Why isn't a structure that does amortized allocation an option here? I appreciate the design goal was "no allocations in steady-state", but that's what you'd expect if you were using C++'s std::vector: After a while the reserved space for the vector gets "big enough".
I'm sorry, I had a whole section on amortized allocation in a draft version of the post but I deleted it, thinking it was tangential. You're not the first person to ask: https://www.reddit.com/r/rust/comments/1gbqy6c/comment/ltonq...
And my response: https://www.reddit.com/r/rust/comments/1gbqy6c/comment/ltpv0...
One typical approach is double-buffering the allocation but it doesn't work here because you need to pull out the waker list to call `wake()` outside of the mutex. You could try to put the allocation back, but you have to acquire the lock again.
I had an implementation that kept a lock-free waker pool around https://docs.rs/wakerpool/latest/wakerpool/ but now you're paying for atomics too, and it felt like this was all a workaround for a deficiency in the language.
Intrusive lists are the "correct" data structure, so I kept pushing.
I had the same question. I would think that with rust’s Vec, no allocation would occur at steady state. Vec does not automatically resize when removing elements.
Yup, you can even pre-allocate a given vec capacity to not start zero-sized.
Yeah, I'm also not sure what the author is talking about, std::vec also does amortized allocation and never shrinks. There are a bevy of more complicated vector classes in Rust if you need different behaviors, but off the top of my head I don't know why std::vec wouldn't work here.
For me, it really is just so much syntax and mental overhead to track. Somehow it seems like the same goals could have been accomplished with less verbosity.
It almost feels like an engineering tool/language, by devs for devs, instead of by devs for “customers” where customer == “outside” devs, of which I am one.
Edit: typo
Safe rust is harder than C.
Safe C is harder than safe Rust.
That used to be true, but now we have reasonable model checking tools for C. It's possible to write safer C without the cognitive load of Rust.
https://www.cprover.org/cbmc/
I am sure it is “possible”, but we are talking about practicality here.
Why doesn’t the Linux kernel embrace model checking instead of experimenting with Rust?
Great now give me tooling of this millenium (no, I am not going back to vendor everything manually and I am not reinventing every basic data structure I wrote in University in ever project I work on) and we have a deal!
Oh, also get rid of header files, they are archaic. And I want fearless concurrency... And sum types!
If you want those things, you don't want C. Pick a reasonable higher level language you like that makes those decisions for you.
My comment was not to imply that somehow C is superior to X, Y, or Z, but rather to point out that the safety problem with C does have a practical solution.
Fair point, sorry (non native speaker here, for what it's worth) have a wonderful day!
Zig is easier than Rust.
Safe Zig is harder than safe Rust, but easier than safe C.
In conclusion, programming is a land of contrasts.
I've never heard the opinion that safe Zig is harder than safe Rust. Pretty much always the opposite, that Zig is easier than Rust all around.
Dosnt GCC have flags that enforce memory safety? Also don't fuzzers handle this issue?
It cannot possibly. You can catch some low hanging fruit, but asking a compiler to evaluate whether a specific chunk of code is memory safe is basically solving the halting problem.
Can you explain what that low hanging fruit is (or refer me to docs), and also explain it being a decision problem a bit more thoroughly. I will accept that if you have to run a program to decide if it's memory safe then that fits the criteria, but from my understanding static analysis doesn't run the program, and a compiler is parsing and lexing anyway so it should be able to catch at least some things (the low hanging fruit)?
Since I have actually started using C I realized how easy it is to be lazy and not handle memory right so it makes Rust and maybe C++ seem more appealing, but trying to figure out random segfaults it seems like address sanitizer and valgrind catches more than I would have assumed is a low hanging fruit.
I guess I should look more into how Rust manages that safety or understand what memory safety is trying to accomplish more formally. I've taken GC for granted for years until I needed to care about memory.
(Not your parent)
An example of low hanging fruit is -fwrapv. This flag takes a behavior that is undefined, signed overflow, and converts it to defined behavior, two's compliment wrapping. That improves safety, but it does not prevent all errors. There are many flags like this, but they all tackle individual aspects of the problem, and even if you turn them all on, there are situations which aren't caught.
Thanks. Yeah that makes sense for low hanging fruit. Going through the gcc flags it does seem like a lot of tradeoffs have to be made so you can't cover everything. A quick look through compiling Rust it seems it does at least some of this checking at MIR. I'll have to read more about it.
The compiler really doesn't have the opportunity to simulate every possibility of code. It's not just the matter of whether the function is safe, but every possible use of the function, which the compiler may not see when it is focused on a single compilation unit or a library.
If you want this level of safety, which is possible in C, then you need to use a model checker. Model checking C isn't as trivial as adding a flag to the compiler, but it can be done with about as much overhead as unit testing, if a reasonable idiomatic style is followed, and if the model checker is used well.
It is still a decision problem, and thus has similar limitations, but you can perform steps to ensure that you have some level of soundness with unwinding assertions and other techniques.
Thanks that's helpful I'll take a look at model checking.
They’re incomplete, and running msan significantly slows things down, to the point you’d be better off with Java.
Maybe this depends on the application type. I've written a lot of each, and maintaining anything involving concurrency is worlds easier in Rust, since those tend to be the most painful and time-consuming bugs to fix. It's got a learning curve, isn't a particularly easy language, and a big chunk of the "community" sucks, but most of the places I've applied it have been a net hassle savings.
Is that really the case? I would say writing bug ridden C is easier then Rust in some cases. Writing working C is much harder then writing safe Rust.
> Using a Vec means we allocate memory every time we queue a waker. And that allocation is taken and released every time we have to wake.
Is that so? On every push back? I’d expect it’d only do an allocation when the current array segment is almost full… as a vector you might write by hand or like the ones in the C++ standard libraries do.
Rust is harder than C
To me C is much, much harder than Rust.
I've written both, albeit way more Rust.
What makes C much much harder? What concepts exist in C and are not present in Rust?
It's not about concepts, it's not about language constructs but the overall work and mental load working with the languages.
For me it's header files, no package manager, doing pointer magic all the time (void pointers are... Brrr!), concurrency, ...
I could go on. I don't enjoy it, although I like simple and easy things.
C is simple, but not easy
Can't wait for Swift to gain traction in systems programming.
I feel a sort of professional obligation to learn the cryptic unnecessary glyphs and runes that Rust and its ilk proliferate all for this important concept that is memory safety.
However, I would much rather that more languages like Go and Zig simply take off in popularity instead, and that we just reject the eyesore and cognitive mess that is Rust syntax. It’s a language which has no regard for beauty.
For what it's worth, I disagree. I think it would be difficult to design a language more elegant than Rust while accomplishing the same goals. Maybe those goals aren't ones that everyone cares about, and that's OK.
It's a language that's definitely worth learning; it expands the mind in a way that languages like Go (or Java, or any GC language) will not. There is a great beauty and elegance to its design.
I would love a much simpler Rust...a Zig with a borrowchecker, a small stdlib (like Rust), some official package manager.
I agree that Rust has a lot of syntactic warts but the common stuff I find beautiful
brace yourself. rust people are coming.
If you aren't nice to rust people, they'll get RCE after overflowing your buffers and make you a furry.