Why Not Rust? (2020)
matklad.github.io
matklad.github.io
I think the author wrote off "dependencies" to quickly. I'm kind of unsure about the state of dependencies. A lot of libraries seem to be peoples hobby/free time projects. I ran into strange comments in libraries where people don't actually seem to understand the domain they're writing the library in. Like I think all of the http frameworks are pet projects?
My feeling after using rust and async was that it kind of tricks you into writing sub optimal code often. Like what could be solved with a singleton ends up needing a lot of boilerplate, new data structures, complex code so you end up writing in some other way, or burning your self with mutex.(aside note, everybody recommending a faster mutex that's not in the stdlibrary seems odd). I also dont know why npm and node get so much shit for needing so many libraries but rust and cargo get a pass for these small libraries everywhere.
> I also dont know why npm and node get so much shit for needing so many libraries but rust and cargo get a pass for these small libraries everywhere.
Because cargo is a better package manager in every aspect.
A piece of software can only help with some of the less important questions when you're working with many dependencies.
Also there are no namespaces and integration with other languages boils down to writing our own build.rs scripts.
In Rust, when you compile an earlier edition with the newer compiler it actually works
It is not. For example, cargo doesn't have namespacing, which makes the issue of name squatting worse. I'd like to have every crate of the tokio-rs organization on Github (https://github.com/tokio-rs) under a @tokio/ namespace or something like that. npm allows that.
Dismissive responses like that are part of why people talk about "blind optimism and fanboy-ism".
If your criticism is that there isn’t a lot of maturity in the ecosystem, then I would agree. The author also makes this point.
There will always be something the industry is excited about. Rust will eventually find its place and its hype will be eclipsed by whatever new trend/language is cool 4-7 years from now.
Having shared mutable state, or doing bare pointer arithmetics is "morally wrong" in the sense that it's a constant source of bugs in production, sometimes dangerous. Any language that reminds you about that is doing a useful thing; C++ is very much capable of that, too.
Shared mutability can enable some bugs, like the existence of a CPU enables bugs, but that doesn't mean CPUs are morally wrong.
If incrementing doesn't always increment you are in for a bad time after all.
You are always going to have data that is shared and changes. Any non trivial system will. Rust just discourages the naive "just point to the heap" method that tends to work until it doesn't.
That is the problem not just with Rust but with today's tech. There is moral story or ideology everywhere. My definition of "engineers" is basically trade offs. Unfortunately that doesn't seems to happen much in software. The Rust Evangelism Strike Force.
There was an era when any rust code that use Unsafe is considered "morally wrong" regardless of whatever technical reasons or personal preference.
Thank goodness cargo audit (for https://crates.io/crates/cargo-audit) in any case.
Isn't this right for any language, though?
The lack of a stable ABI is also a problem. For example, on my Linux distro, if a library has a bug, I just update the library and all software using it are fixed. No need to recompile everything.
So, essentially, the cargo system makes it easy to import weak code and hard to fix it later.
I am not saying that cargo is terrible, but it is a problem with rust that is not to be dismissed.
You need to rely on other people's code. Whether it std lib or not. Left pad is because of:
A) ease of modularization
B) problems with tree shaking
C) people want small easily understandable library
Which tbh works more often than not.
----
Stable ABI is a curse as much as blessing. Once you have stable ABI, you will never want to break it. Even if breaking ABI would be beneficial long term. See
https://cor3ntin.github.io/posts/abi/
And if you desire Stable ABI there is option of exposing C API in Rust.
----
> So, essentially, the cargo system makes it easy to import weak code and hard to fix it later.
Hard for who? Cargo can easily fork a package and recompile it. It makes it harder for distros. But you can ship static linked dependencies, easily.
I use Rust and I also use C/C++ and other languages as appropriate for each situation.
The fanboyism is a huge turnoff for me, too. But the anti-Rust complaints are also getting old.
Really, I’m just getting tired of everything in software engineering being turned into a competition, as if we’re cheering for our chosen sports team and we can’t stand to see the other team win anything. So much online discussion is wasted on Intel vs AMD, Mac vs Linux vs Windows, Rust vs C/C++/Go, React vs Remix vs 10 other frameworks.
It’s getting really hard to extract actual useful info from these permanent debates when the fanboys from different sides pour in to compete.
The best resources I’ve found are from people who aren’t afraid to use multiple platforms and acknowledge that they all have pros and cons. If someone can’t describe something in terms of tradeoffs, they’re probably not trying to make an honest evaluation.
Sure one can somehow write Rust-CUDA but is going to have an uphill fight versus someone just crusing along focused on the actual problem, instead of building infrastructure.
That is just one example.
Thanks for the upvote anyway.
The info is still there, you just need to use a different extraction technique.
Those that will be confused and wonder what the hell it’s about is those who come from node/go/Java and think that using http is a “hello world” example of what Rust should do with ease.
> I also dont know why npm and node get so much shit for needing so many libraries but rust and cargo get a pass for these small libraries everywhere.
I think one big difference is the less promiscuous (but more frustrating) dependency management. In node I end up with 15 versions of a popular “should have been stdlib” library. In Rust you usually just have to allow one. So the libraries that form core functionality tend to be conservative about changes and get absorbed into std.
Js obviously doesn’t add things to the core platform very quickly.
This image of Rust as lower level than Go isn't one I agree with, but I can certainly see how people end up there. Go has it's garbage collector and userspace threading model, one gives up a lot of control to a runtime that you don't have to in Rust.
But as the author mentions if we're after good enough speed these details aren't important. So I don't most of the time care if there's a GC or not.
What I care about is the code I have to write, and Rust IMHO offers me nicer abstractions and more features.
The Go world in their desire for simplicity, use a for loop for everything, but in Rust we have the excellent iterator trait. The Go world offers me product types, the Rust world offers product and sum types. The Go world offers some mechanisms for codegen, but it is a far cry from Rust's macros.
Go I consider a modernized C, that is notable more for what you can't do than what you can, and how fast it can be learned - owing to a minimal and already familiar feature set.
I confess that I have a bit of a chip on my shoulder, I have for the last few years been working as a Go developer and in the last few months it's started really started to grate me how much work things are. I really miss things like '#[derive(...)]'.
YMMV though, as everyone uses Scala differently. A lot of Scala code goes in a more abstract direction, but if you use it in a more imperative Kotlin-esque way it's a very nice language.
The case of Java demonstrates they were mistaken: their estimation of their own understanding was grossly outsized. I don't know enough about Go to say whether that is true of Go's designers.
The most popular python and ruby implementations are written in C, right?
Swift is written in C++
It seems not sooooo unusual.
There was a time when doing that helped convince people the language could do practical things, and might be useful because you might not have had a compiler you liked for your dev environment. Nowadays, though, there is so much support for coding compilers in an existing langauge that building one in the immature language means wasting a lot of time, and furthermore makes your language hard to change because maybe the compiler is using something you want to change. There are more impressive things to write in your new language that might be actually useful.
Thus, rustc is (mostly) not coded in Rust. Rust's demonstrator project was the guts of a browser, instead. Notably, that project was coded in a language substantially different from Rust both today and at the time.
Most recently I converted my ancient (1980s era) disassembler into D.
"The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt. – Rob Pike" [1] (emphasis mine)
You and I: we're not smart enough to use modern language features. And it just so happens that the language features and practices that we can handle are the ones that Rob Pike likes and settled on as good in the 70s and 80s. Anything from the last 25 years, we're not capable of understanding.
Honestly, part of many bonuses of leaving Google at the end of the month (after 10 years there) is that I hopefully will have the choice not to use Go and its heaps of insulting boilerplate... but I also hopefully won't have to deal with that kind of arrogance.
[1] http://nomad.uk.net/articles/why-gos-design-is-a-disservice-...
The most used and deployed code at Google is written in C++, not Java or Python, and while we have a sane subset of it in our style guide it is by no means dumbed down to the level that Pike seems to imply we need it to be.
Google has done fairly fine deploying C++ into production for a couple decades. I haven't found Go superior at all so far, more insulting than anything. And tedious and opinionated in all the wrong ways.
For instance, I'd consider Go being very “idealistic” since it was designed mostly from the vision of a small group of people with a clear mantra: “simplicity”. But of course, Rob Pike would never have advertised his language this way.
On the other hand, one could argue that Rust is pretty much is the “realist” camp, since it was initially designed in a very different shape from what it later became when early adopters started using it (the Servo team at Mozilla in the beginning, and then other groups: for instance the Fushia team at google at an impact on the design of the async part of the language).
Obviously it's a massive company and I saw maybe 5% of it, but I joke with my friends that I had to leave goog to get real exposure to Go and borg/k8s.
And yes, I do think generally Go is used more outside Google than it is inside. It's not a language made for Google -- despite what Pike is saying -- it's a language made by some people who happen to work at Google to scratch an itch they've had since they worked at Bell Labs.
Obviously not trying to state anything deep here by over-analyzing that one point in his quote, but kinda funny his statement implies researchers write decent code.
Often I question the result of a paper due to how bad the code is. Many paper results cannot even be replicated by writing your own code following the papers description.
I agree more fundamentally with the fact that most people write low quality code if there aren't structures/patterns already in place to protect them from themselves. From that perspective, I agree with his point. Though I'm not a big fan of the implementation of the idea
He's done great things. Think about the caliber of people that man worked with every day. And then he goes to Google and sees thousands of 'ordinary' people slinging C++ and doing web work. We become products of our environment. There's a lot of gap between him and you. And that's ok. You'll go on to do another 10 years at another big tech company and live a good life.
I'm way below your caliber and I see some arrogance in your words. I think both of you are arrogant because you unconciously became a product of the environment. I'm just a nobody that fails 9/10 interviews, including Google 2 months ago. It takes luck for me to get hired anywhere and I find it hard to relate to the middle class people I work with because I've been so poor. But I bet you I come off as arrogant to tradesmen, cashiers, janitors, and many others. Just let it go.
Greatness by association is not a solid concept. It shows how lucky a person is to be surrounded by great people but in itself does not count as an evidence of his greatness. But on the other hand, great research minds tend to cluster, thus his priors are above average.
I'm not sure if Pike himself holds these opinions, or if that quote was just a slip, but it's not nice. TLDR Pike is a 'great' man, with smarts well beyond me, but that's not of a lot of relevance to the code that we (code mechanics / producers) have to write every day, and I found Go tiring and insulting to work with.
This feels like an odd and unfair comparison.
I have nothing but respect for Pike and what he’s accomplished.
I don’t dislike him in some anti-cult-of-personality. I’m not ignoring good work he’s done just because it doesn’t fit my world view.
But I CAN criticize his individual work for being limiting or lacking ergonomics that I like.
And it’s entirely fair to look at Pike’s personal motivation for making the decisions he made, because it has an impact not only on what Go is currently, but also what it’s likely to be (or likely to not be) in the future.
If Go were created with the intention to be, say, a cleaner C++ with garbage collection, then I’d be all over it. I’d know that even if I don’t fully like it’s implementation today, it might eventually “get there”.
But instead it was created as an admittedly dumbed down language for people that Pike personally decided needed a dumbed down language, so I know that it’s never going to be what I want it to be.
That’s not an attack on Pike as a person. It’s not me blindly following a media campaign trying to destroy someone.
It’s me saying that I benefit greatly from generics and more robust error handling, and Go is not likely to have those in the capacity that I want because the whole motivation behind the language was to avoid those things at all costs.
None of which takes away from Pike’s career in its entirety.
That's false. All of the original developers started writing side projects in Go extremely early on and all of them still write tons of Go including on their side projects. They don't have their ego tied up in their choice of programming language.
Gosling and others have written plenty of code in Java themselves, plenty of Solaris tooling was migrated to Java (a fact that Bryan Cantrill has complained often in his talks), and it replaced their OpenSTEP efforts, with Java EE being a rewrite from Distributed Objects Everywhere, originally written in Objective-C.
That Gosling did not fully understand features cribbed from other languages is evident from how broken the Java version of those features are.
https://cs.gmu.edu/~sean/stuff/java-objc.html
As for your reference to Guy Steel, he was just one person on a large team, which besides the point above was full of ex Smalltalk and SELF people as well.
Maybe learn properly about the Java history?
I'm personally patiently waiting for OCaml multicore, which should have the best from both worlds, except the large communities.
> It also does not have the 100B$ garbage collectors of the JVM, which are a deal breaker, at least for me.
Can you expand a bit on that? I was under the impression that even with all that investment, Java code tends to be slower and more memory hungry than equivalent Go code.
They’re adding value types of course, but I haven’t looked at how exactly they’ll work.
Java also tries to optimize out method dispatch, so we can use it freely where Go might tend to avoid it.
There's some other tricks like tagged pointers, purgeable data it doesn't reliably have either.
I’ve read that games and simulators schedule realtime asset loading pretty carefully; what other problems are solved using large constant data?
Or is it ok to use cgo to work around Go's shortcomings, but not JNI for Java's?
I don't remember how good the Java interfaces look though, eg if you worked around no constant data by defining your arrays in C and passing them back, would they be bounds checked arrays?
If one is dumb enough to use C style arrays instead of C++ bounded checked ones, yes that is a problem, again just like C arrays defined in CGO.
I'm talking about defining a 'const int[]' in C and having it appear as an 'int[]' in Java. I think that's even less likely to happen if you define a const std::vector.
Actually I've never even seen someone define constant data in C++ using a vector, but this fits my experience of C++ developers telling me I'm stupid when I do the only thing I've ever seen anyone actually write.
Go doesn’t need a good GC as much because it can rely on value types sometimes. But for general enough workloads a superior GC can triumph.
Also, Java is memory hungry only in that doing GC when it is not absolutely needed is useless work. The JVM is actually quite power efficient due to trading off memory for better throughput — so especially in server environments with huge (up to multi-TB) memory which the JVM is free to use, it can be ridiculously fast. So no, java is not slower than equivalent Go program though correctly comparing languages is nigh impossible.
Yes they are Windows only, but lets be honest most pure .NET shops aren't moving away from Windows, as .NET cross platform has left part of the commercial ecosystem behind and no one is willing to port those stacks into cross platform ones.
Rust is a clear replacement of C++, but with the flavor of OCaml and even Haskell added to it. It requires thoughtful planning, a good grasp of what you are building, how it should work, down to the lower levels, and significant knowledge of the CS fundamentals. Relatively few parts of the industry appreciate such an approach, but where it's needed and productive (see e.g. control software, system-level software, well, web browsers) it shines, and has no serious competition.
Possibly architectural progress in libraries will one day make writing web services in Rust ergonomic enough to compete with Typescript, much like auto-ptr / unique-ptr made writing C++ less painful.
I do like Rust and it has worked well for this particular project. The problem, though, is that now there's a Rust mania that has set in and a desire to write/rewrite lots of stuff with Rust. Based on what I've seen out there on the web, this seems like a common thing. While Rust offered a clear benefit to us for the WebAssembly work, for every other case the benefits of Rust are mostly absent or minimal but the drawbacks around losing a garbage collector, losing a large healthy package ecosystem, losing use of our existing internal libraries and tooling, etc. loom large.
So I am in an unhappy position where I keep telling enthusiastic Rust advocates no. Another interesting thing is that people tell their friends that we are using Rust and now they want to apply to work for us. We have a hard time recruiting and there's part of me that wonders if we'd just be flooded with applicants if we started advertising Rust jobs. This is somewhat tempting - we might be able to easily hire teams of enthusiasts to churn out Rust but for how long? How long will it have this magic aura and following? I certainly don't want to be left with tons of crappy Rust code in a few years that nobody wants to maintain.
I feel like HN has mostly been very enthusiastic about Rust. A thread critical of Rust is rare and I feel like it is refreshing.
The best thing about rust is that rust attracts more people to learn systems programming languages. And thus, there will be more system programmers.
Nowadays, young people favor application programming far more than system programming.
But the problem is there are not that many system programming opportunities.
Then they decided to rewrite the existing systems with rust. We can find a lot of these examples on GitHub. Who knows if it is just a moment of enthusiasm, and how long will it last?
As for not happening before, Delphi and Turbo/Object Pascal on the PC and Mac worlds beg to differ, unfortunately C++ adoption from the OS owners pushed them away.
Executable bloat in Rust applications is generally due to a lack of "idiomatic" support for dynamic linking (.dll or .so objects). You're expected to pretty much write your own interface layers using the C ABI, and it's especially hard to make these "interface" layers play well with generic code, which is quite ubiquitous in Rust.
This is one big problem for me, too. I don’t think those young kids have any clear idea on what they are doing. I’ve seen the same thing happening with Python in the past. Heck, I remember some kids who went far enough to use python as their default shell. Such enthusiasm, but pointless. It took almost 10 years since the beginning of the Python hype until it actually dethroned Java.
Same for Rust. It does have a fine edge - I’m tbh excited a little - but that’s just it. Rust taking over the world? It will only after people got tired of calling it cool.
I think it is generally impossible. While first draft version of the program may have similar velocity, later maintaining will necessarily hit those leaked lower level detail which is simply not a problem with managed languages.
But I don’t think that this is a problem in any way or shape.
= blind optimism.
When it comes to migration, optimism can be far far worse than pessimism. The hurdle for adoption is much higher for existing projects than new projects.
You don't understand what the memory safety promised by Rust actually does.
(No, it won't save you from buffer overruns. Only from segfaults.)
With a buffer overrun in C, you can smash the stack or other interesting structures, and put executable code of your choice in it. If you have the right kind if container breach and privilege escalation exploit lying around for the target OS, you can pwn the entire infrastructure eventually.
And no, I don't think you can memcpy / strcpy data[*i++] with abandon in Rust without first marking such code unsafe. To say nothing of concurrent modification which is another popular source of index overruns, and more.
As for the rest of your comment - good job on completely missing the point.
When we talk about "buffer overflows", privilige escalation exploits are the least of our problems.
https://hackernoon.com/null-the-billion-dollar-mistake-8t5z3...
Telling your boss that the frontend is splattered with 'undefined' and all the balances are unsynchronized, but, hey, it's not a bug because no segfaults were involved in the process is a very good way to get fired.
In general I'm very much against any rewrites without a clear list of things that are lacking in the current implementation, details on how the rewrite would address this and what are the functional requirements for the rewrite to succeed.
But regarding new projects I think that people sometimes underestimate Rust in non systems programming scenarios. Not saying that you do, but at my job there is the notion that Rust is only good for "close to the metal" performance and where usually C/C++ would have been used. I think this is a flawed view. So while I am aware Rust is not the best choice for some stuff, if I had to write a web service and/or microservice that's not rendering anything I would often choose Rust just because of all the correctness guarantees. I come from a Ruby background, but I've done a lot of other languages too and a confidence I have when writing Rust is a big factor on why I like the language so much. I'm not really sure what percentage of Rust devs work with web services, but I think in the next few years it will be a big percentage of work done in Rust.
The blog post about "Why strings seem hard in Rust" was not met with excited reactions.
Personally I wouldn't be writing web services with Rust, I don't think the language was really designed with that problem set in mind.
As a replacement for Go if you want a comprehensive type system instead of a skinny one.
If you are into MS or AWS ecosystem there are also SDKs available for their clouds.
But there are a lot of people in IT with experience in other fields that gets on into the marketing sales pitch that is trendy since the Java days, and i'm not blaming the community of doing anything wrong here, giving its the expected behavior of tech communities nowadays. But there is this noise into how Rust is sugared that i dont think its true, in that its the first system language that is as productive as a mid-level normally GCed language like Java or Go while giving more control and performance.
I think there is no language into that spot yet, but i think that something that follows Rust could achieve this (Its something in the middle of Rust and Swift).
The control and performance are there, as happens to C++, but the productivity is basically the same, which is not very good as compared to other languages that target more of being productive than being in control.
The control level pay out in the end in the quality of the final product, but it s not the best option for things that dont require the amount of control that C++ and Rust gives you.
But the thing with Rust, C++, Zig, etc.. is that, when you need it, you need it. There are things that can only be done with those kind of languages, and often are very complex and high-performant piece of software.
Its not for everyone, and you need a sort of commitment that you would only pay if you really need. So writing things like 'web services' on them is not optimal, and you should use languages like Go, Java et al. for this.
Tech people should be able to navigate hype and marketing lingo and see the things for what they are, i think is the same that is happening with 'web3' right now.
This is not very fair to Rust. It's absolutely more productive than C++ when you account for the productivity benefits of "not shooting yourself in the foot" with pesky memory-safety bugs. Let's not even talk about scripting tools like Python, Ruby or JS that were never designed for "programming in the large" yet have somehow become popular in that role (with predictable consequences wrt. technical debt and lack of long-term maintainability).
The proper, challenging comparison is with correctness-oriented GC languages like Haskell, OCaml, possibly F#. And Rust might nonetheless have the better community, since it can address systems-level and performance concerns out of the box unlike those.
For a 'stable' production implementation after prototyping, I agree that the benefits Rust provides (if it compiles, it will either panic or work) are well worth it, and so longer-term, I buy that you probably will spend less time, but in my experience, things like not being able to specify default field values inline (you need to provide a 'new' function, or 'default' impl, meaning you then have very tightly-coupled parts in different places in the code) compared to C++11's member variable inline initialisation is very fustrating when in the early stages of prototyping, when things are changing a lot.
Similarly, as mentioned above - string handling, with as_str(), to_string(), String::new(), from(), etc, I think make some sense at a full production level when you might care about ownership / copies / allocations, and want them to be explicit, but when you're just prototyping stuff to work out data structures and relationships, it's increadibly annoying IMO when you're refactoring.
Rust's "struct update syntax" can be reused to do this. `MyStruct { my_field: 42, ..my_local_defaults }`, where my_local_defaults is an instance of MyStruct that's in scope.
In C++, you can set the default at the same time as you declare the member variable, meaning the three things (variable type, variable name and variable default) are in exactly the same place in the code on the same line.
In Rust, you can't do this, you need to implement new() or default() (or manually specify all field items when declaring the struct), which is normally in another place in the code, so you have tightly-coupled code in two places, which is as bad as C++ headers and implementation files which is what a lot of Rust evangelists seem to complain about on here about C++ (it's a valid complaint, but I don't think Rust completely gets it right, as is complicates / makes things more verbose in other ways like this one).
I’m cautiously optimistic that Rust will get this at some point. There’s quite a lot of demand for this in the Rust community (although some opposition too)
The key reference on "modern" C++ is the "C++ Core Guidelines". They're pretty comprehensive but quite complex as well, and they don't really manage to establish a "sound" or "safe" idiom for the language; they're more focused on avoiding the most common pitfalls.
Once upon a time C++ was supposed to produce "code bloat". Now it doesn't, without any changes to address that.
I have spent strictly more time in the last five years filing compiler bugs than chasing memory usage errors in my C++ code. So, whatever benefits the extra work needed to make rustc happy, and time waiting on slow, slow builds is supposed to deliver, it fixes no problem I have.
The same is probably true for anybody coding C++ using modern library facilities. There is just no temptation to do anything dodgy. It would be more work than the safe thing.
So, the marketing insists on these benefits, but I don't see them. It is likely old code was more prone to such bugs, but we are talking about coding now, not then.
Rust has lots of nice features to make coding pleasant, and you can evidently get used the nannying, so it seems like an OK choice for a project that nobody else will need to maintain.
std::string_view is but the latest example of a "modern" C++ facility that turns out to be a dodgy footgun, due to the lack of safeguards around lifetime errors. C++ still lacks a general equivalent to string_view for arrays (what Rust calls a slice) for the same reason. These seem like significant drawbacks to me.
maybe because your code isn't worth having people actively attack?
It makes sense. A common use case for C++ today is writing high-performance software, where the software architecture is likely to be schedule-driven (e.g. thread-per-core, userspace I/O, etc). In these cases, you see minimal heap allocation, shared structures, or locking. Modern C++ is pretty safe by default for this; it allows you to do unsafe things, and to do safe things that the Rust compiler deems unsafe, but there isn't a cabal forcing people to write non-idiomatic unsafe C++ code in software architectures where the code is naturally almost entirely single-threaded and with few heap allocations.
The most insidious bugs I see in modern C++ code bases are scheduling bugs e.g. logical or behavioral edge cases due to pure async execution, which can be very subtle. Rust doesn't help with that.
Apart from that, everyone even half competent would know that and make a trade-off involving other factors. Being dogmatic about a single facet isn't helpful.
When doing generic application level programming: use STL containers, iterators and algorithms properly and you won't have any of those pesky bugs. Does not require much efforts.
If you do want to shoot yourself C++ will of course offer thousands of ways for you to do so. But with modern C++ it has to be your choice. Unless of course you are working on some specialized system type code, libraries like STL and other similar tasks.
I'm pretty sure that a lot of people will be more productive with Rust as compared with C++ as with others will be the opposite. So i guess in by making an average over those two crowds, i doubt they will differ that much.
The sort of bugs Rust will avoid that are kind of hard to debug, giving it doesn't happen that often is the problem of shared state in a multi-thread environment.. the other kinds are often easy to spot in C++, the only difference is that they are caught in runtime.
Conscientious programmers aren't going to spend too long in the shooting themselves in the foot phase.
To me the awful compilation model and horrendous compile times are much bigger productivity bummers.
I enjoy helping folks debug memory safety bugs that stumped them. It takes the drudgery out of the day. And it's usually an opportunity to teach them something.
Rust does not force you to do this. You can in fact pass around `String`, `Vec<…>` etc, and Rust will use move semantics to make it reasonably efficient. Of course using &str and &[…] is the proper "zero overhead" solution, but it does require some extra effort.
Some expect String while others expect &str.
Once you understand why, it’s easy to intuit which one should be used where, but it was a day 1 speed bump for me.
Like, if you have fn f(s: &str), you can do:
let x: String = String::from(something);
f(&x);
You can see this in playground here [1] (click "Run")To explain: both String and &str is essentially a pointer plus metadata, but &String is a double pointer (a pointer to a pointer). Rust knows how to convert double pointers to pointers, using the Deref trait. This implicit conversion is applied only in a handful of places, and only if it doesn't lead to any ambiguity (if f received a generic parameter it wouldn't work for example).
If you wanted to convert String to &str explictly, you would need to write &*x, like this (see in the playground here [2]):
let x: String = String::from(something);
f(&*x);
That's because if x has type String, *x has type str (because String impls Deref<Target = str> [3]) and &*x has type &str. This means that auto-deref changed your &x into &*x automatically.[0] https://doc.rust-lang.org/book/ch15-02-deref.html#implicit-d...
[1] https://play.rust-lang.org/?version=stable&mode=debug&editio...
[2] https://play.rust-lang.org/?version=stable&mode=debug&editio...*
[3] https://doc.rust-lang.org/alloc/string/struct.String.html#im...
It really doesn't. It doesn't even have native support for bitfields. When I tried doing low-level programming in Rust, this immediately struck me. Rust's semantics don't really offer much of an improvement over what already existed in this area. In addition to presenting all kinds of new, frustrating challenges. I've used Ada extensively in this area, and I just don't see what Rust has to offer that Ada didn't already address. I'm all too aware that string operations in low-level programming languages are always going to be prickly. Ada's standard library, even in its 80s incarnations, is light-years ahead of Rust in this area. Interoperability with other languages is another area where Ada beats Rust hands-down. The first thing I tried to do was integrate C and Rust, and realised immediately that it was a nightmare. In Ada, it really couldn't be simpler.
Many people would consider C bitfields half-baked in the first place, given that the feature is well-known to be underspecified in a way that leads to compatibility/portability concerns. Is this really the kind of stuff that Rust should concern itself with?
This is coming from someone who uses C bitfields every day on an embedded MCU.
... which are actually an incredibly common, totally everyday problem encountered by tens of thousands of developers everyday?
> autogenerating handlers with svd2rust works great and provides a little bit of safety along with it.
I don't think that requiring third-party tooling to implement something so basic is a suitable solution to this problem. What if the manufacturer of the device I'm working with hasn't released an SVD file? What if I'm not actually working on an MCU at all, but a peripheral driver?
I never said they're umcommon. As I said, I user them all the time. They're incredibly common in specific fields. There are many systems programmers who barely ever use them.
There are a lot of footguns with bitfields in C. (1) they know nothing about defaults, (2) they have no way of restricting values beyond bitwidth, (3) they are often inefficient, producing multiple load-modify-stores for what is really a single operation, (4) they are not endian portable.
svd2rust addresses all of those: (1) it knows what the defaults are and you can tell it to load them for you, (2) it will restrict the values for each field to only those that are valid (you can use unsafe to bypass the check), (3) it produces a single load-modify-store (or less) for any given operation, (4) it produces shifts and masks internally so doesn't depend on the compiler to behave as expected.
Of course, C bitfields don't require tooling, and I'd say they're a little easier to use. It's a trade-off, and I personally prefer the rust side.
> I don't think that requiring third-party tooling to implement something so basic is a suitable solution to this problem.
Don't most people auto-generate bitfields, too? Like sure, writing a couple by hand is fine, but I didn't think that's typical.
> What if the manufacturer of the device I'm working with hasn't released an SVD file? What if I'm not actually working on an MCU at all, but a peripheral driver?
You can write your own SVD, or write yaml and convert that to SVD. I think that would probably work for a peripheral driver, too.
I think there is an opportunity for a systems language to do bitfield types right, which means not copying C.
I don't see the relevance in pointing out that the C standard has problems related to bitfields. Rust is supposed to be an improvement upon C.
Look, it's not that hard. Just write getters and setters for the underlying structs, and you'll pretty much reach parity with what C gives you (but in a well-specified way). There are crates that will make this easier.
Memory safety guarantees that don't go out the window if you ever need to free a dynamic allocation, for starters.
Bitfields are an underspecified minefield in C, I don't think it's as clear of a loss as you're making it sound.
I don't think anybody is saying that? Particularly given that Rust is working on precisely that.
The argument is merely that not currently having a good bitfield solution is hardly a showstopper for systems development, given that C is already far more popular in that area than Ada despite its deeply deficient version of bitfields which leads to them being banned in a great many codebases.
Lots of people like to repeat this without understanding all of the other controls that Ada has for access types (pointers).
Ada access types (like pointers) are typed so you could make multiple equivalents of `Foo` that can't be mixed and matched. e.g. one version of `Foo` might be for one subsystem and another version of `Foo* for a different subsystem, which would prevent you from giving an allocated object from one system to another. You also can't point an access to any arbitrary location without it being specifically marked with `all`, and there's limitations to how you can use an access that gets passed as a parameter. Due to these factors, how allocations can be used gets constrained and with Ada's support for RAII ("controlled types"), these details usually get hidden internally within types. It's not a specific block section like `unsafe`, but your unsafe memory usage is helped by the type system.
Rust has limitations as a systems language but this isn't one of them.
[0]https://rosettacode.org/wiki/Memory_layout_of_a_data_structu...
I agree Rust should eventually have some native support for bitfields, but only if it's a high quality spec that addresses the outstanding concerns - Rust should specifically avoid the C mess, which isn't usable for most projects.
But this doesn't mean bitfields can't be done in Rust; it's just not in the stdlib (yet). See this post[0] which contains a survey of various libraries (and their respective limitations), and this slightly older article [1]. Those articles somehow missed the modular-bitfield crate [2], which is perhaps the nicest to use, and is what I would recommend you.
If you don't care about the specifics you can get any crate discussed in the above articles and use them.
About interoperability with C (and C++), I really like bindgen, cbindgen and cxx. The bindgen book [3] is straighforward. This section of cxx documentation [4] also describes the tradeoffs between the different FFI approaches. I don't know Ada though, so I must ask: what does Ada do?
About this,
> I just don't see what Rust has to offer that Ada didn't already address.
Off the top of my head, the crates ecosystem, the borrow checker, sum types with pattern matching [5], a stellar story for error handling (beats any language I know hands down - that is, if you use crates like thiserror and anyhow).
[0] https://immunant.com/blog/2020/01/bitfields/
[1] https://myrrlyn.net/blog/misc/bitfields-in-rust
[2] https://crates.io/crates/modular-bitfield
[3] https://rust-lang.github.io/rust-bindgen/
Again, like so many others in this thread, you're talking about C's problems. C is not the only language to implement this feature. I keep bringing up Ada because it's a language that features the ability to define structures at the bit level in its core language semantics. It manages to do this in a portable way, with a much more sophisticated, not to mention sound type system.
> But this doesn't mean bitfields can't be done in Rust...
Why should users need to rely on installing third-party additions in order to do something so basic like this? With the addition of third-party frameworks, and tooling you can make C as safe as Rust too, so I don't think the argument that third-party dependencies can solve this problem are relevant. I'm judging these languages simply on how well they allow me to express the semantics required for low-level programming.
> About interoperability with C (and C++), I really like bindgen, cbindgen and cxx. The bindgen book [3] is straighforward...
Ada comes out of the box with this functionality. It doesn't require reading a 'book' to understand it either, it's very straightforward.
> beats any language I know hands down - that is, if you use crates like thiserror and anyhow...
Again, why should someone need to install third-party tooling and dependencies for such basic features? The fact that so many responses list a litany of external dependencies to address Rust's shortcomings does not lend much credibility to Rust's design.
1. It lacks a good way to control memory in different ways for different data structures. The allocator_api is still experimental. This feels strange for a systems language.
2. It's a complex language. There's no way around that. The developers have done a great job avoiding unnecessary complexity, but the benefits rust offers do have a complexity cost and you should really evaluate whether rust enables you to make up for it with simplicity elsewhere in the stack.
3. The sync/async situation feels like a division to me. Maybe that's inherent and not rust's fault, but it does feel like some kind of a divide. For instance, if you want a postgres driver, rust-postgres is great, but it follows the async approach and that means zillions of dependencies and long fresh-compile times. If it used a synchronous style, it would only depend on the standard library, and compilation would be very fast. Should there be two libraries? I don't really know.
4. There's not a good supported way to build "plugin" style shared libraries where you can refer to symbols that exist in the program that loads the library. You can hack the build flags in, but none of the testing infrastructure works.
5. While C integration is great, there's not a good way to catch a longjmp() from C without some undefined behavior.
6. The culture of rust seems to favor dependencies a bit too much. The decision to take a dependency is an important one with trade-offs, and the rust culture seems to be always to take a dependency, even if doing it yourself would be little code, and the dependency transitively includes a ton of code that seems to churn a lot. This plays to rusts strengths around tooling, but goes to far in my opinion.
But after all of that, there's still one huge reason to pay attention to rust: if you are a systems programmer, there aren't a lot of options for most projects, so having a new option is a big deal. In many cases, the practical options go from "C/C++" to "C/C++/Rust".
best luck to rust programmers, it's just too much for me and too late for me. I do use quite a few nice rust utilities(fd,rg,etc) daily.
I write embedded firmware, replacements for GNU coreutils, system services, and more in rust and I love it. But I write my web apps in C# and I wouldn’t change that.
I'm looking forward to have something like Linux written in Rust though.
Hanging out at community Discord I see that there are a lot of new guys, students. They just want to experiment, and I am all for it.
Yes, maybe it's not the most efficient thing from "business perspective", but maybe, eventually something new and exciting will come out of it.
Programming is evolving, we had a lot of the same functionality available in older languages, using older paradigms, but eventually those stay in the past for a reason.
I'm staying open minded and I am excited for the future.
I feel like D really deserves to be mentioned more often when talking about Rust alternatives. It's sort of the anti-Rust: improving on C++ by focusing on ergonomics rather than raw efficiency. It's likely more familiar to the C++ native than Kotlin and Go, and more directly comparable to Rust in that it has an expressive type system, compiles to native code, and interfacing well with C.
I would say D is the best answer on average to the question "I like all those things Rust can do but I find it annoying to work with, what can I use instead?" whereas Kotlin or Go will not always be a great stand-in.
So it remains a very nice language, but you need to be willing to get hands dirty fixing bugs and contributing back, if you feel going into production with it.
All D success stories had their of fork of the D toolchain, or standard library, to certain extent.
More than a sucess story, I consider Symmetry the very last hope to keep D relevant actually.
Second, Rust doesn't stop you from handling memory allocation errors. The standard library might fully support failable allocations but that doesn't stop you from using your own collections for that type of work.
Third, this has been an item of interest for a long time in the Rust community and is actively being worked on as part of introducing Rust into Linux.
Being ill-equipped to use or recover from allocation failures in such arenas bodes poorly for a systems language.
I gather there has been some effort to get Rust library components aware of such things. Handling exhaustion in them is an interesting research topic.
"nobody"
~/chromium (main)$ rg --stats -t cpp "[=\(][ ]*new "
...
17639 matches
17536 matched lines
7162 files contained matches
96561 files searched
1539692 bytes printed
763109193 bytes searched
1.119265 seconds spent searching
1.428974 seconds
That's surely a lot of non-usage. Maybe it's just Chrome, though? ~/llvm-project (main)$ rg --stats -t cpp "[=\(][ ]*new "
...
7447 matches
7394 matched lines
2245 files contained matches
35148 files searched
541938 bytes printed
385115019 bytes searched
0.425689 seconds spent searching
0.485431 seconds
Hmmmm.... which we already knew existed.
In the event you're as desperate for those last dregs of performance as the author of the "Why not rust?" article, (and let's assume you actually measured your performance problem before jumping to the step where you make everything more complicated and more buggy for no good reason?) you can even Box::try_new_uninit_in() and unsafely do all the initialization in place as needed yourself as you might do by default in C++.
Now, I don't know when Allocators will be stabilized, the Working Group has been making steady progress for some years now but this is a tricky business, witness the many languages that get it quite badly wrong...
I hope it wasn't a safety-critical application.
> Rust [...] doesn't stop you from using your own collections for that type or work
and your own replacements for all of their dependances. Remind me why I need Rust again.
> this [...] is actively being worked on
I'm looking forward to learning Rust when it becomes worthwhile. For now it seems that asking about allocation errors in Rust on HN is like asking about nuclear waste disposal for next generation reactors. Answer: never mind about that; look at all the cool safety features.
Take a look at https://docs.adacore.com/live/wave/spark2014/html/spark2014_....
You can prove the absence of memory leaks, too. :)
> Values of such an access type can be deallocated safely. GNATprove generates verification conditions to ensure that no memory can be leaked.
So... you can deallocate safely AND verify that no memory is leaked. Is it not wonderful?
- https://docs.adacore.com/live/wave/spark2014/html/spark2014_...
- https://docs.adacore.com/live/wave/spark2014/html/spark2014_...
> SPARK builds on the strengths of Ada to provide even more guarantees statically rather than dynamically. As summarized in the following table, Ada provides strict syntax and strong typing at compile time plus dynamic checking of run-time errors and program contracts. SPARK allows such checking to be performed statically. In addition, it enforces the use of a safer language subset and detects data flow errors statically.
I'm nitpicking, but it's due to considering the difference important for some unsuspecting programmers: having control over data layout and runtime behaviour doesn't grant you maximal performance. It grants you capability for achieving minimal overhead, which is connected to, but a different thing from performance.
Cargo is great but it’s not continued to evolve fast enough. For example, there’s not enough defence against supply chain attacks yet.
>> Rust’s price for improved control is the curse of choice
The annoying unstated side effect of this is that on average, developers will choose the easiest option. E.g. quite a few blogs and tutorials wastefully use clone because it’s easier than wrangling with lifetimes (which admittedly have an developer-unfriendly habit of being quite virulent through your code) - or whatever other approach you fancy to avoid pointless copying. They do it just to get around the borrow checker. You don’t need to be processing that many strings with this approach before Python’s (lack of) speed is giving you a run for your money.
Something I could have written in 3 lines of JavaScript. In Rust, it requires knowledge of poll, pin, boxpin, ... and even third-party crates (if you yourself are not enough of a magician). I'm not sure if Rustaceans are okay with that additional complexity, but it really scares me off to dig deeper into that language. I would appreciate if some experienced Rust programmers could comment!
Is the Java written in the 90's still the same Java written today? I feel the mentioned Lindy effect equally applies to dialect. C++ today is radically different to C++ from the 90's. I'd venture COBOL from the 90's is likely quite similar to COBOL today.
PERFORM x CONCURRENTLY WITH y BUT NOT WITH z.
000100 MOVE 4 TO reference-count ATOMIC IF reference-count EQUAL TO 3.I eagerly watch the progress of GUIs and other architectural work in Rust because they are helping to raise the popularity of solutions we've been ignoring because things have been "good enough".
People want a be-all-end-all tool, but Rust is just not that, so you get tons of arguments about this or that use case. You wouldn't use a saw to hammer in a nail. Just treat it like a tool.
1. The mental overhead is larger than most want to admit. It's not about the syntax, but about memory management of course. The time and effort spend there should be spend to solve the problem at hand.
2. Depending on a lot of third party libraries which are maintained by just 1 person.
It is compiling from scratch on mac m1 in around 7.5s [2] and that's +100k lines of Rust. However there is close to none dependencies, so this +100k is all there is to compile pretty much. Still, this is impressive feat!
[2] https://twitter.com/rikarends/status/1467529091284934666
Deck PDF: https://qconlondon.com/london-2017/system/files/presentation...
Take this with a grain of salt: Go data races can still lead to memory corruption and security vulnerabilities (no one uses the inbuilt data race detector in production, few use it while testing).
Why Not Rust? - https://news.ycombinator.com/item?id=24536645 - Sept 2020 (319 comments)
Yeah; I felt that pain too.
I tried to write something to address some parts of the missing space. It's still in the early stages but you may be interested:
A lot of projects choose C, C++ or Rust only because they need performance:
* Backend services
* Numerical computation
* Game development
* Rendering engines, e.g., browser engines
System programming is usually needed for what we consider Operating Systems, e.g. kernel, system libraries, and shell.
In my career as a C++ programmer I have worked on a lot of projects that I would otherwise write in C#, Java, Julia, or Go.
> Compile Times
This is (IMHO) the biggest problem with Rust as the article mentions. Apparently, this was due to some early unfortunate design decisions that resulted from the tight coupling with early Rust and Servo. Rust at this point is heavily tied to LLVM (I'm aware there's a gcc version in the works).
If you don't know, the general structure is LLVM is to have a frontend and a backend. The frontend translates whatever your language is into LLVM-IR (intermediate representation), a sort of abstract assembly language. This isn't tied to any particular architecture (eg IIRC there are infinite registers in LLVM-IR). The backend translates that to a particular architecture. This is good because it's easier to get a compiler off the ground but gcc's performance is still second-to-none.
Rust expands macros and the likes by producing a godawful amount of LLVM-IR and lets the backend strip out what's not needed. This works but impacts compile times.
More details here [1].
> Complexity
Yes and no. It's a bit like how dynamic programming languages originally rose to prominence because of the speed you could do things, or at least that was the argument. Static typing was seen as getting in the way or being too complex. I've come to the conclusion that this is a false economy. The best way I can put it is in Python you end up writing unit tests for spelling mistakes.
The hierarchy of programming errors is IDE errors are better than compiler errors which are better than runtime errors. In an ideal world there's no difference between IDE errors and compiler errors but almost always there is.
So Rust is actually forcing you to move runtime multithreaded issues into being compiler errors. This feels like more work (because it is) but it is infinitely preferable. Opponents will say, for example, that C++ has smart pointers to denote ownership. This is true but they have a runtime cost. Rust doesn't. And the effect of a runtime error in C++ is typically a core dump or just weirdly inconsistent behaviour (which is often worse).
People routinely underestimate just how complex multithreaded code really is, particularly when they try and write lock-free abstractions (as is popular in C++) especially given the complexity of how processors actually work (eg out-of-order execution, branch prediction). There are various tools that are written to identify some of these issues (eg valgrind, asan, tsan, etc). While I was at Google I remember reading about how a bug had been found in an in-house written lock-free abstraction with some new tool that had been >7 years in production.
But yet, even on HN, I too often hear "what's so hard about just putting a mutex on it".
[1]: https://pingcap.com/blog/rust-compilation-model-calamity
Rust has full set of smart pointers: Box (unique_ptr), Rc, Arc (shared_ptr), and in addition to it RefCell to work around limitations of borrow checker. All of these are very widely used in almost any real world project.
Virtually every modern compiler is implemented using this approach - including GCC. GCC also used to be quite a lot slower than clang, but just like with error messsages, the GCC people have put a lot of work into it.
With reference counted strings/arrays, it has solved almost all memory safety issues for me.
Besides, leaks are only a small part of memory safety anyways.
And I'm not surprised that you find the biggest leak in Javascript, the language that probably the biggest percentage of amateur/inexperienced developers are working in. (which is not generally a bad thing, but increases the chance for these bugs)
That Pimpl is not practical in Rust is interesting news to me.
The author's blithe unconcern about Cargo dependency trees looks unwise just now.