ESR is someone and I am no one but this deserves a response.
As someone else mentioned, the two complaints here fall into (a) Rust is severely hard to use and (b) there is very poor documentation.
The learning curve is high. I couldn’t compile anything for a day and a half when I started learning it. But when it clicked, it clicked and it was smooth sailing in an extremely pleasant language. The ownership model is only as hard as C++ RAII, so if you know that, you’re fine. You just add the fact that pointers can’t live past the thing they point to. And it’s substantially simpler than C++ for what it accomplishes. In fact I don’t know how you could write a language any simpler than Rust’s with the same performance and safety characteristics and abstractive capacity as Rust’s… Perhaps D is a good competitor. But I also don’t know everything so I can only speak to what I’ve seen perdonally.
I don’t know what Eric means by a strange ritual for concatenating two strings, but I’ll give him the benefit of the doubt that it must have seemed unusual and unnecessarily obtuse. But I also think that if you’re new to an opinionated language, even if you’re a famous UNIX programmer, everything seems strange. Personally, I just look at the documentation for Rust’s String, and see there’s a method “push_str” that takes a pointer to the String and a string slice (what amounts to a pointer to another string). That seems identical to the type signature of C’s strcat! There’s also an overload of the + operator that does the same thing.
I find the documentation is superb. I think how good the documentation is partially explains why Rust has become really successful in terms of getting reasonably popular as fast as it did. The “book” (it is much shorter than a book and is organized into topics) explains everything I need to know about the language. Any holes in my understanding are filled immediately by helpful (and polite!) community on IRC or Reddit.
Rust is a year and a half old since 1.0. I say that not as an excuse, but as an accomplishment - what a language (and ecosystem, and set of tools, and documentation, and…) for such a short time since stabilization! This comes with the caveat that some things that seem like glaring holes might actually be very well implemented outside of the standard library… by people who work on the language itself! For instance Alex Chricton and others have just recently released Tokio 0.1 (the “one-stop shop for asynchronous code”), and he’s on the Core team, the Libraries team, AND the Tooling and Infrastructure team. This is not third party, this is allowing for rapid iteration and development of new parts of the language’s standard lib before having to make the real, non trivial commitment to maintain it forever by putting it in Std.
Go has also had 7 years since its version 1 release. That doesn’t meant comparisons are unreasonable but it does mean that a little nuance and benefit of the doubt should be given to Rust during a criticism.
And by the way: the design decisions Rust has made has given it the ability to provide significantly more throughput than Go can achieve, all without a garbage collector. That is a good counterpoint to any comment about Rust not having green threads.
An issue I had with regards to documentation is that sometimes I find myself trying to write a thing with (for example) Vectors in “C style”, running into ownership issues, and then looking at the Vector docs.
And not really finding which of the 40 trait implementations let me do a variant of what I want.
I think a “Rust idioms for C programmers” would be excellent and helpful. “Here’s things that you can do in other languages, not in rust, but here’s how to do it in Rust”
For example, how do you read in a number N, then a list of N strings (newline separated), and have those all be in an array? Or rather, what’s the most idiomatic way to do so (that steers clear of un-necessary muts or copies)
I’m glad you’re sharing your experience because anecdotes like these improve things for everyone, as well as provide a corpus for people to get predigested answers.
What kind of ownership issues do you typically encounter with vectors?
There are a lot of traits. The most important ones all get a mention in the book, but sometimes it is annoying to look at the docs for a type and wonder if the behavior you want is hidden in a trait listed below.
What sort of idioms do you mean? In my view it seems easier to just start with teaching a Rust perspective and then answering the questions that arise that way, because they’ll be more idiomatic questions than asking people to convert concepts or techniques. But I see the value in a Rosetta stone.
Here is a minimal example of reading an integer and then reading that many strings into a buffer.
let mut buf = String::new();
let stdin = std::io::stdin();
stdin.read_line(&mut buf).unwrap();
let n: u32 = buf.parse();
buf.clear();
for _ in 0..n {
stdin.read_line(&mut buf).unwrap();
}
“In fact I don’t know how you could write a language any simpler than Rust’s with the same performance and safety characteristics and abstractive capacity as Rust’s”
Try Cyclone, Modula-3, or a subset of D with the borrow checker added to any with a GC. Cyclone was one of Rust’s predecessors aimed at C developers. Modula-3 was my favorite alternative to C++ given safety & compile speed. As a Wirth-style language, it has almost no learning curve. The D proponents in Rust discussions usually parry what features Rust people bring up. All that tells me is D does a lot but with syntax & design aiming for C or C++ programmers more. Should reduce learning curve.
Has anyone thought through what Modula-3/D looks like with a borrow checker? IIRC, Cyclone doesn’t have lifetime annotations. Since I don’t know any of these languages, it’s hard to say what they would look like, but at least in Rust’s case, the complete absence of lifetime annotations would hurt abstraction power pretty significantly.
Cyclone’s region-based memory management & special pointers were a partial inspiration to Rust’s scheme. The modifications probably wouldn’t take much outside coding style. Modula-3 with a borrow checker would probably just be much simpler than Rust. That’s more my point. D I have no idea about except to say it’s a safe, expressive language that I’ve never seen people gripe about like Rust’s learning curve. Code samples I looked at look comprehensible since I used C & C++.
Type systems has the annoying property that “just adding one more feature” tends to invalidate the metatheoretical properties (such as type safety, or the decidability of type checking and inference) that are the entire justification for using a type system in the first place. It’s a mistake to think that you can retroactively add features to a type system without suffering negative consequences. When Java added generics, the design turned out much uglier than it would have been if generics had been a language feature right from the start.
Another (complementary, rather than competing) approach to writing safe programs that I feel hasn’t been given sufficient attention, is to use a language with an axiomatic semantics. In such a language, it isn’t the language implementor’s job to reject meaningless programs. Rather, it is the language user’s job to make sure the program he submits has a meaning. And the meaning of a program is whatever you can prove using the language’s axioms and nothing more, so there’s no need, unlike the C and C++ specifications, to say “such and such has undefined behavior”.
I think my point is that I’m skeptical of the idea of “just bolting on a borrow checker.” It’s a significant addition and it’s not clear what else in the language under consideration would have to change. I just simply don’t believe that you can say, “simpler language + borrow checker = simpler than Rust” so easily. :-)
I’ve worried about this possibility before. You may be right. Maybe I’m just being optimistic that a simple, safe, and imperative language would at least make it easier. Who knows until it’s actually attempted, though.
I realize that. I thought there was something that Rust had to add that Cyclone didn’t have, and I thought that something was lifetime annotations. But I could be misremembering. I don’t know Cyclone well enough.
I did mention that D was a good competitor in this regard. Modula-3 severely restricts the user in a number of ways like being unable to pass pointers to stack variables, which is critical for zero-copy and non-allocating code, as well as doing plenty of runtime checks and providing a garbage collector.
D is a great language, but the last time I checked it still couldn’t generate a runtimeless library that could be called from C. if it had that and basic algebraic datatypes with pattern matching I’d start using it immediately.
I’m not fluent in Rust, but I think the author forgets that C itself has a really high learning curve.
I remember learning C. It was hard. To use one the author’s own examples: Would you say that concatenating strings in C is a breeze? I wouldn’t. Should I use a statically bounded buffer with strcat(), strlcat() or strncat(), but then what if the result was too long? Or I could use a heap allocated buffer, but then I need to remember to free it again, and if I don’t the language won’t tell me and I have a leak. If I get it really wrong then I corrupt memory (possibly) resulting in a crash. Joining two strings in C is far from str1 + str2 as it is in many languages.
The language demands a huge amount of fussy, obscure ritual before you can get anything done.
Are these rituals not in-place to prevent you from making mistakes like the ones I mentioned above in the string concatenation example?
Then I found out that a feature absolutely critical for writing network servers is plain missing from Rust. Contemplate this bug report: Is there some API like “select/poll/epoll_wait”?
You can poll sockets using the mio crate, it’s just not part of the core language (yet).
Overall, from this article I get an air of “I don’t like it because I am unfamiliar with it, and I have unrealistic expectations of a systems programming language that isn’t C”.
I’ve spent a couple years writing most of my code with rust now, and I’m still being smacked by the learning curve on a regular basis. I haven’t found a way to dive deep and then be able to blast code out without a war with the compiler, like you can do with scala with a thought-dense book like Functional Programming in Scala (I felt like I was constantly smashing my head into a wall for a year with scala until I read this). I keep going with rust because I feel like I’m cultivating future opportunities to contribute to something that replaces widely deployed infrastructure with something that takes the keys (exploits) away from governments and organized crime, which is quite motivating to me.
I admit to not being a much of an ESR fan, but this actually seems like a fairly reasonable post.
I gave rust a try a while ago (shortly after 1.0 shipped, as I recall), and at the time, I didn’t find it a very pleasant fit for the kinds of things I wanted to do (network services (http, tcp, udp)). Maybe I just didn’t “get it” or “didn’t give it enough time”. Not sure.
I currently use Go (or python for the occasional rest api), and find it “pretty ok”.
Makes me wonder what languages I will be using over the course of the next 5 years though.
Server-side Swift? Will D finally break out? Crystal? Elixir? Nim? Pony? Myrddin?
I’m seeing a TON of activity in Elixir. Its Ruby-like syntax and real erlang derived MP model seem like a really solid value prop with a relatively shallow learning curve.
The question is what systemsprogramming language will we be using. There’s a ton of comfortable high level languages, but by contrast there are only a handful of systems programming languages. It’s 2017, and I think people are growing tired of buffer overflows, and are super keen to wave manual memory management goodbye. But then GC doesn’t suit systems programming either. This is the “niche” rust is trying to address, but we have yet to see if the learning curve will be its demise.
I’m looking forward to a time when the phrase “systems programming” doesn’t implicitly throw out the idea of a garbage collector. I’d personally like it if Go was a little bit more strict about stuff like checking for err, but if someone said “Hey, we’re going to write an operating system top to bottom in Go” I’d be all for it. Whatever inflection point we needed between user expectations and hardware limitations that called for no garbage collection we went past a long time ago.
I could have maybe entertained the idea that my phone would need it, but my phone has such horsepower now that manually managing object lifecycles is a much smaller issue than just straight up business logic bugs.
Maybe the network stack for a real time online video game. But if we’re doing that, we don’t need a brand-new language to do it; safety isn’t the concern there.
Are you sure about that? I don’t doubt what you’re saying, but I’m hearing and seeing an awful lot of movement around go even in contexts that would have been previously considered to be “systems programming”.
I agree there will always be a niche for non gc languages that need max to the metal performance, but how big that niche is has yet to be determined IMO.
As to Rust, I think most people agree there’s an enormous amount of untapped potential there. It’s unfortunate that some large company with associated large $$ and man hours doesn’t back the project. I think that would help smooth some of the rough edges and make the adoption experience a bit smoother.
Elixir has a shallow learning curve. OTP (the main benefit of BEAM languages) doesn’t, especially for web developers used to Ruby and Node. It’s not “hard” or impossible, but like Rust, it’s not Python.
Anecdotally, I’d say Elixir feels about 2-3x faster than Ruby or Python in the single-threaded case. But just about any Elixir app will use multiple processes (Erlang processes, not OS processes), and at that point the benefits of concurrency quickly leave Ruby and Python in the dust.
EDIT: A couple of benchmarks say roughly the same thing I do:
I think raw Elixir is faster, but the problem is that every real world library or use case of Elixir will use OTP in some way or another (at the very least, a GenServer, which is admittedly very easy to learn).
I think ESR is right and wrong here. Right because Rust isn’t the right tool for what he’s trying to do. Wrong because Go isn’t a C replacement, and he never needed C in the first place for what he’s doing.
I think Rust is a great language–I use it for developing the runtime system of a research PL I’m working on–but the community oversells the value of GC-less operation. Most of the “complexity” of Rust stems from having the type system track references. This bugs me because for many people Rust will be their first introduction to a “good” type system, and I don’t want people to get the wrong idea that typeful programming is all pain little gain.
I don’t think there is anything in particular about an NTP server that requires a GC-less language. Rust sans borrow checker would probably be a fine, easy-to-use choice. I thought about unpredictable GC pauses interfering with synchronization but apparently Go works for him so that can’t be an issue.
I don’t think writing an OS kernel and a server have much in common. I think Rust is a fine language for developing a kernel in. If by “OS” you meant all of the supplemental stuff that isn’t running in kernel mode, then I agree, but then again I don’t think C is an appropriate language for it either
Synchronizing your system’s time to millisecond or microsecond precision at least requires a degree of control over your GC, since FOSS real time GC implementations aren’t a thing to my knowledge. Which is why ESR is considering Go.
There’s nothing special about Go’s language design wrt GC, and its implementation uses a standard collector aggressively optimized for latency at the expense of throughput. The same principles apply to any other language, whether or not existing languages make this trade off in their collectors is irrelevant to my point. The way you word this sounds like it is intended to be a rebuttal, but I see it as supporting my claim that GCed languages are quite appropriate for this domain.
Not many people want to or need to hack on kernels, I would think servers, including much of “all of the supplemental stuff that isn’t running in kernel mode” would be a much bigger domain for Rust.
Maybe? I’m not particularly interested in spreading the gospel of Rust.
Or may I turn this implicit question around? What domains do you think Rust is suited for?
I would use Rust for anything I would use C for.
Embedded programming, where you need tight control over allocation
OS kernels & drivers
Language runtimes (I include Servo in this)
Libraries that need to make minimal assumptions about their runtime environment so they can be used from any language
Games, and more generally anything A/V. The people I know in game dev tell me that using C# (Unity, etc) is a pain and they have to be very careful about controlling allocation rates
I think Rust is a great language–I use it for developing the runtime system of a research PL I’m working on–but the community oversells the value of GC-less operation.
Is this research PL public? what about the implementation?
I’m working on a language looking at mutation and aliasing control with some similar ideas to Rust,
but focusing on a higher level such that I can afford the overhead of a GC.
Most of the “complexity” of Rust stems from having the type system track references.
Yes!
At times it feels like all Rust cares about is gc-less memory safety, and that all the other safety guarantees are happy byproducts.
I think a similar language with a good gc would be a better fit for most use cases.
As a mild aside:
I also think Rust deals with internal mutability poorly (it is invisible to the user of a potentially internally mutable object).
Rust community folks (@steveklabnik et al.), please use your mailing lists or whatever to get your street preachers on the same page about not sounding like jerks or fanatics:
Rust isn’t Python — you need to do some studying, read the book, and read the documentation. There are a lot of concepts in here that you won’t find in most languages you are familiar with. There’s algebraic types, lazy iterators, higher-order functions, traits, lifetimes, and a borrowing and ownership model. These are all necessary and have allowed Rust to become the powerful language that it is — no garbage collector, no runtimes, no overhead.
The “high barrier to entry” is just you being long time C coder reluctant to change your habits, investigate and understand things with open mind.
You probably also missed the way how cargo version management works which makes it perfect for reliably reusing 3rd party libraries that solve all other issues you’ve mentioned (like mio for portable epoll, mioco for green threads).
To be fair, there’s a lot of really disgusting nonsense in the other direction, but this sort of posting is why a lot of folks view the language claims with skepticism.
If you’re going to quote a negative comment, you should also consider linking to any one of the numerous constructive comments by Brian Campbell.
Rust community folks (@steveklabnik et al.), please use your mailing lists or whatever to get your street preachers on the same page about not sounding like jerks or fanatics:
Despite what you may think, we don’t actually believe we can eliminate all fanatics everywhere. It seems weird to hold us culpable for that. We do our best on home turf, but no, we don’t moderate ESR’s blog.
I don’t think you get it–for humans, negative experiences are going to outweighs positive ones every time.
Every time somebody says “I have these problems with Rust” and more than none of the replies is “Rust is great works on my machine git gud” you all look like assholes.
See also the massive and pervasive success of Haskell.
I don’t think you get it–for humans, negative experiences are going to outweighs positive ones every time.
I get it quite well. I understand the power of perception. I’m not talking about humans. I’m talking about one very specific instance: your comment. I mean, really, you’re going to claim that people are skeptical about Rust because of inflammatory comments made on ESR’s blog, of all places? It defies credulity!
You’re also insinuating that Rust moderators and community leaders are somehow responsible for every little thing uttered by random people on random blogs. This isn’t about me denying human perception, this is about me calling out your nonsense. Street preachers? Really? Give me a break.
Look, I get that I’m saying negative things about your tribe, and so of course you’re going to be less-than-charitable in your reading of anything I say. This would also be a great time to wear your hat, to at least make it obvious to everyone that this is the case.
I mean, really, you’re going to claim that people are skeptical about Rust because of inflammatory comments made on ESR’s blog, of all places?
No, I’m not going to make that claim. I’m sorry if that’s how my wording came across–what I meant to point out was that this was a glaring example of the sort of behavior that is pervasive on places like here, HN, and various other programming communities.
For the reason listed above, I understand why you’d like to interpret my complaint in the narrowest-possible (and hence easily dismissable) context, but that’s really not what I mean and I suspect you know it.
You’re also insinuating that Rust moderators and community leaders are somehow responsible for every little thing uttered by random people on random blogs.
Ruby has this great saying “Matz is nice and so we are nice”. Other communities have had similar results. Some communities (Linux) have leadership that set the tone for acceptable discourse. So, it’s totally possible to have leaders set standards and lead by example–and I refuse to believe that the Rust folks (especially given that a few built their careers on being outspoken) couldn’t somehow do a similar thing.
Street preachers?
I believe the analogy is accurate. You aren’t paying them, they do their missionary work in public, and sometimes they’re annoying.
Again, I don’t hate Rust or anything–I just prefer seeing more useful descriptions of why it’s awesome and concrete examples of it being used.
I had a longer comment written out, but I thought better of it.
This would also be a great time to wear your hat, to at least make it obvious to everyone that this is the case.
I wear my hat when I’m speaking in my capacity as a member of the libs team. I’m not speaking in that capacity with you. I’m not putting on my hat every time I mention Rust.
Again, I don’t hate Rust or anything–I just prefer seeing more useful descriptions of why it’s awesome and concrete examples of it being used.
If you’re allowed to say that you want more useful descriptions, then I’m allowed to say that I want fewer inflammatory analogies from you.
steve and everyone else who moderate and manage the rust subreddit, forums, and irc channels do a wonderful job disallowing negative and zealous commentary while fostering a productive and respectful space for discussion. I have had a very pleasant experience in all the rust spaces and can assure you the message is already out there.
For me the appeal to rust would be ML style but fast, but Rust feels a hair too far from ML languages for me to get really into it. The most egregious example for me is Currying/Partial Application in rust. It feels like I can get close to how I program but not quite there. ESR I imagine is much more the C/C++ programmer who doesn’t get why there’s all this functional nonsense in his new language. Of course the real reason is, it’s great, and make a lot of stuff simpler. I’ll keep an eye on rust as time goes on, once it gets proper partial application I’ll give it a shot.
This is unfortunate… I’m thinking that subsetting C++ with a new compiler might be the way of the future. Bjarne is working on that [1], and maybe there is some overlap with the boringcc idea [2]:
I feel that such a project would be an order of magnitude less effort and have better adoption characteristics than Rust. It boils down to writing a naive C++ or C parser and code generator. You don’t have to make it fast because the goal is to make it secure.
And then generate object files which can be linked into objects compiled with LLVM, so you can interoperate.
I appreciate Rust’s effort, but unfortunately I think history has taught us that languages are adopted because of what they can do, not because of what they can’t. All the warts in C and JavaScript opened a lot of doors to “creativity” by outsiders, like metaprogramming with the C preprocessor and libraries like jQuery.
C and JavaScript are “by default you can do it”… Rust is “by default you can’t do it”, which means there are huge holes in the ecosystem.
And honestly I think that “rewrite in Rust” is one of the worst strategies for making software secure that I can think of. It is crazily expensive and only rules out a subset of bugs. From what I can tell, ESR understood this and spent a long time cleaning up the existing C codebase.
It’s kind of a “run before you can walk” thing… if you have tons of dead code from 1995 or 1985 lying around, I’m not sure what makes you think that you will have time to rewrite in Rust, when you haven’t even done basic hygiene on your project.
Although to be fair, the Corrode Rust-to-C translator is a counter-argument. But I bet it would be a lot simpler if Rust was designed from the start to auto-convert from C, rather than designing the language first, then tacking on the converter.
I’ve found with my shell project [3] that adhering to compatibility can actually improve the language design. It imposes constraints and lets you learn concrete lessons from existing code.
(Also, I have to point out that although I enjoy ESR’s writings, he has insanely poisonous people on his blog…)
I’m thinking that subsetting C++ with a new compiler might be the way of the future. Bjarne is working on that
Has Stroustrup or anyone else working on the C++ Core Guidelines made any official statement about the kinds of misbehaviors that they rule out? If so, is there a proof sketch that the statement actually holds? Heck, is there even circumstantial evidence that the statement is plausible? If not, what makes these guidelines any better than the gazillion unsound static analyses that already exist for C++?
Experience tells us that we mustn’t blindly accept anyone’s word regarding a programming language’s metatheoretical properties (like type safety). Even the most competent and well-intentioned language designers make mistakes. Here are some examples among languages intended to be type-safe:
I feel that such a project would be an order of magnitude less effort and have better adoption characteristics than Rust.
I have no idea about “adoption characteristics”, which are ultimatley driven for social reasons. But, from a purely technical standpoint, I’m pretty confident it’s at least one or two orders of magnitude easier to write a resource-correct program in Rust than an equivalent one in C++. It’s difficult to compete with “errors are located and traced back to their exact source”.
I appreciate Rust’s effort, but unfortunately I think history has taught us that languages are adopted because of what they can do, not because of what they can’t.
Ruling out nonsensical runtime behaviors is most certainly something a language can do, whether you appreciate it or not.
I think you are missing my point, which is precisely about adoption characteristics. I’m claiming that Rust’s design goals are laudable but will cause it not to be adopted.
Pascal ruled out many more undefined behaviors than C. But Pascal lost and C won, precisely for this reason. Sometimes you need to do weird stuff. People underestimate the diversity of use cases programming.
If Rust’s goal is to anticipate all use cases, then it will fail, because nobody or even no one group can do that.
ESR mentioned something about being able to select over only a fixed number of channels? That reminds me of Pascal’s choice of fixed length strings. It makes it easier for the compiler to enforce certain things, but harder for the programmer to get his or her work done.
I presume if you have fixed length strings then you never have buffer overruns. But that is not the right solution to the problem – programs need dynamically sized strings!
Although I disagree with a lot of what Jon Blow says, look at his Jai programming language. He talks about “80% solutions”, and the fact that the cure could be worse than the disease. The problem is that Rust has 100% guarantees in some places, and then 0% in other places. I believe that a more balanced design will have 80% solutions and cover a wider variety of use cases.
ESR mentioned something about being able to select over only a fixed number of channels? That reminds me of Pascal’s choice of fixed length strings. It makes it easier for the compiler to enforce certain things, but harder for the programmer to get his or her work done.
I’m not sure that’s a great example, since channel selection on std::sync channels isn’t even available on stable Rust. Moreover, this is a library quirk rather than something that is inherently limited by the language.
What’s interesting is that Go doesn’t support selecting on an arbitrary number of channels either. You need to write select with a fixed number of cases that can’t vary at runtime. (OK, so this is a lie, you can actually use package reflect to build a select at runtime with an arbitrary number of channel selections.) Why was this glossed over? I don’t know. shrugs
One of the key things missing from this blog post is due diligence on the state of async IO in Rust, since that clearly seems to be what he was chasing based on him wanting an epoll interface. The blog post links to an issue that is over two years old as the definitive last word. Now, to be fair, while I think the OP did not do their due diligence, that doesn’t mean the Rust ecosystem doesn’t share any of the blame either. We need to make this stuff more discoverable. We will, but it’s still early days. On top of all that, there is literally nothing stopping anyone from using epoll directly just like you would in C. So I think this stuff about Rust “preventing” you from doing things is pretty misguided.
My main point is this: I wouldn’t read too much into ESR’s blog post. There’s definitely something to be learned from it, but “Rust is going to fail because string concatenation is hard” or “Rust is going to fail because std::sync::mpsc doesn’t support dynamic channel selection” ain’t it.
The problem is that Rust has 100% guarantees in some places, and then 0% in other places.
I don’t think that’s true. If someone hasn’t built a safe interface around some C library, then someone can just go out and build it themselves. Rust gives you the abstraction power to bundle an unsafe core into a safe interface.
Pascal ruled out many more undefined behaviors than C.
Pascal is in many ways a more disciplined language than C, and that’s a good thing, but it doesn’t really rule out any unwanted runtime behaviors that C doesn’t.
If Rust’s goal is to anticipate all use cases
It isn’t. There exist use cases for which Rust is woefully inadequate, and that’s fine, no language has to be good at everything. However, there’s no use case where data races and uses-after-free make sense. Or maybe I just lack imagination.
ESR mentioned something about being able to select over only a fixed number of channels? That reminds me of Pascal’s choice of fixed length strings.
This is a standard library problem, not a language problem.
The problem is that Rust has 100% guarantees in some places, and then 0% in other places. I believe that a more balanced design will have 80% solutions and cover a wider variety of use cases.
It’s fine to criticize those 0% guarantees in other places, e.g., the lack of integer generics really hurts. But why would you want to reject 100% guarantees whenever they’re available?
Addition:
I presume if you have fixed length strings then you never have buffer overruns.
For the record, this isn’t true. Getting the 20th character of the string “Hello, world!” is wrong, whether the string is statically or dynamically allocated. Statically allocated strings merely make it easier to detect potential buffer overruns, but by itself doesn’t rule them out.
Because language design is about tradeoffs. 100% guarantees don’t come for free – they hurt the experience in other areas. Language features can’t be considered in isolation.
100% guarantees don’t come for free – they hurt the experience in other areas.
“Experience” is a subjective psychological issue that I shall not address. As Dijkstra said, we must separate the issues of correctness (relative to a specification) and pleasantness (relative to the user’s whims).
But, yes, 100% guarantees in programming languages don’t come for free. They have to be proven, just like any other theorem. And proving theorems is hard work.
Language features can’t be considered in isolation.
That greatly depends on how orthogonal your feature set is. Not all languages are like C++, where everything interacts with everything else in who knows how many ways.
I’ve seen worse. You can easily send any sufficiently fancy type checker (GHC Haskell, Scala, Rust, C++, etc.) into an infinite loop without even trying. Even Hindley-Milner type inference, which is as simple as it gets, runs in exponential time in the worst case, as the following program shows:
fun f x = (x, x)
fun g x = f (f x)
fun h x = g (g x)
fun i x = h (h x)
fun j x = i (i x)
(* You have survived so far. Now brace yourself. *)
fun k x = j (j x)
But the real problem, in any case, is how to come up with program designs that don’t requite too much effort to verify (not necessarily using types).
If you actually read that article, the author is bemoaning the lack of type classes. (And by “dictionary”, he actually means what a C++ programmer would call “vtable”.) He’s not trying to work around safety measures in Elm’s design.
Swift makes a special effort to resolve literals to types conveniently. This is not an issue with the power of the type system – which relates to what is expressible – or the functionality of the type checker – which relates to what is checkable – but rather to overloading literals in a fairly ambitious way.
Many typed languages will infer that let i = 1 implies that i is an integer or number of some kind; and ditto with strings. Typically with numbers there is a little juggling to determine which kind of number i is – like if it is an UInt32 or an Int8 – depending on how it is used later in the program. With strings you can do the same thing. A literal like let url = "https://web.com" could be inferred to be a URL and not a String if elsewhere in the program it is used as one.
Swift tries to do this for structured literals – arrays and dictionaries – which is pretty tough since for every literal in the literal you have some branching. In most languages you just couldn’t even do that – have it infer the type of your dictionary for you – you’d have to write the annotation. But this is a case of a feature other languages just don’t have, not something that is particularly awkward or slow in Swift.
Say what you will about ESR, but the guy generally isn’t regarded as ignorant when it comes to programming.
My key take aways from this are the following:
A language with a library to handle basic things you get with C from Unix, such as select(2) or epoll support is much more superior to a language which outsources it to, even if well done, an external repository. The reasons for this being the lack of clear support into the future (10 year plan), and lack of clear choice amongst a swath of similar libraries.
Rust is still hard to learn because the documentation isn’t clear, or focuses on the wrong thing.
I must say, I have experienced frustration with 1 in languages other than Rust. OCaml and Lua both suffer from this, for sure. And, even with Rust, a couple of months ago, there wasn’t a clear HTTP client I should be using. I didn’t pursue a project because of (my perceived) friction – I used Go instead.
I haven’t, however, found 2 to be true. Now, I won’t claim that Rust is trivial to pick up, at least not in comparison to someone with a C background picking up Go. But, I certainly didn’t have trouble building a few small >100 line toys after a little bit of reading, admittedly some hands on frustration, and compiler error searches. Luckily, all of the compiler error searches I encountered were extremely easy to search for, and it cleared everything up quite quickly.
All this being said, I want to adopt Rust over Go, because I prefer the philosophy, but between work, which is a large Go code base, and home projects (a mix of Python, Racket, C, Go), I simply haven’t found the motivation to dive into it. Maybe there is actually something to 2…
Wait, why do you say he’s not generally regarded as ignorant in programming? His primary technological reputation comes from text files he took over updates for, and essays on culture. His code is not good.
Well wait, then why do you have a strong opinion on the subject? Why in fact would you have any opinion at all?
The existence of the fact that programmer maturity is possible in three decades does not mean it happened. I’ve looked at the code. He attempted to reimplement the 64 bit integer data type by hand in ntpsec and failed. I have junior devs working for me who, if 64 bit integer types weren’t already a standard on every platform for the last 17 years, would create bug-free (re-)implementations every time. In fact, I can’t remember having a dev working for me in the past 26 years that would struggle with it. Of course, every dev in the last 17 years would look at me like I was an idiot – which is the other part of the problem.
fetchmail (which was initially developed by Carl Harris) is not an example of good coding or good design - in fact, it’s quite the opposite. It can quite easily lose mail in cases where it shouldn’t.
See, for example some of the comments on the getmail FAQ. Yes, I know that getmail is a “competitor”, but those are fairly damning. And don’t forget this - “As to fetchmail: it is an abomination before God”.
Matthew Garrett (who I know has his own axe to grind with ESR), has posted some negative comments about the quality of his software. See this tweet for example.
I do! The man writes usable code. I have no opinion on how elegant it is, but it’s functional.
I think it’s important to separate the person and their opinions from their work. esr’s work speaks with a loud clear voice, even other aspects of his persona do not :)
I have some philosophical issues with some of his edicts (particularly ‘silence is golden’ which I think is the biggest train wreck EVER in just about every way I can think of) but you can’t deny that he’s produced several bodies of code used by a heck of a lot of people.
Can’t honestly say I’m his biggest fan, but credit where credit is due.
The Rust community looks to be settling on https://crates.io/crates/tokio for the select/epoll use case. His other argument is a little more understandable. And there is no denying that for the task of writing a simple IRC server Go is going to get you up and running faster than Rust. However for something like NTPSec I’d argue that the extra safety Rust gives you is worth the tradeoff on ramp up speed.
See, I’m inherently untrusting of anything that follows that statement, categorically.
I remember getting the first edition of the Programming Elixir book by Dave Thomas, where he confidently asserted “the community this” and “the community that” on patterns that were pretty obviously not set in stone or were personal style choices.
It’s waaay too easy for a few bozos who troll message boards to pretend to speak for the entire community (and no, the irony is not lost on me while making that statement). Especially having been burned by the JS ecosystem where “The Community” was 300% behind Backbone as the way to^W^W^W^WKnockout as the^W^W^WAnguler^WMithril^WReact as the way to do things.
Like, we’ve got to do better than mob-driven-development, because the reputation of “The Community” as a thought-leader is (or at least should be) thoroughly tarnished.
There’s nothing interesting about the phrase “the Rust community …” It’s a description of reality. It’s saying, “I’m linking you to this resource, and this resource has significant buy in from a lot of people.” People value that kind of information. If you don’t, then just say that.
I think it’s worth recognizing that particular phrases are frequently abused for rhetorical purposes - usually because there’s some ambiguity in the phrase itself. And in my experience this happens a lot with “the X community” - people frequently say “the X community Y” when they’ve only actually spoken to a couple of X, and from their perspective they’re not lying, but it can all too often be the case that most X don’t actually Y. (Indeed the implicit assertion that there’s a single cohesive X community is, in my experience, almost always wrong). I’d put a lot more credence in a specific statement like “Z out of W of developers surveyed at RustConfUS listed toiko as their preferred async solution” - or even just something like “I’ve got 20 Rust projects starred on GitHub and 5 of them are using tokio” gives a much clearer picture of what “Rust community” they’re talking about.
I’m interested in the Elixir patterns from the Dave Thomas book that are currently not common Elixir patterns. I skimmed the book, but I don’t remember anything strange or now considered bad practice. Elixir has had widely-used conventions from the start.
Safe Rust cannot cause data races, or uses-after-free for several kinds of resources (not just memory). To be precise, safe Rust can have these problems, but they will ultimately be traceable back to unsafe Rust code (or language implementation errors).
Exactly. The Rust type system allows you to give much stronger guarantees that are compile time checked than rust will give you.
I like both languages. Go is very much a frictionless language for me. I feel very fast when I’m writing with it. But if I’m writing software who’s purpose is to offer a strong security guarantee I’d look at Rust first.
Rust has a much more expressive type system which reduces the amount of copy and pasting, dynamic reflection, and ad-hoc code generation you have to do. It forces you to handle error cases (via Result). No nil (via. Option). No data races thanks to the borrow checker.
I think there is a lot of evidence that suggests otherwise. His book The Art of Unix Programming is rated 4.1/5 on amazon. His (accepted) contributions to things like GNU Emacs, Python, as well as a number of tools such as gpsd, fetchmail, reposurgeon, etc, etc suggests otherwise. Now, it’s easy to say, “so? The code isn’t any good!” and that’s fair, but a bit misguided. We live in a world where programmers of languages with no protections against errors, unsurprisingly, make errors and choose abstractions that we later find problems with. When that happens to you, I don’t call you ignorant, point, and then laugh at you.
I don’t mean to defend ESR, because, frankly, someone will assume I support his politics, which I don’t.
But, I am defending the idea that we need to find a way to separate a person’s identities sometimes to create unbiased opinions on an individual one (identity).
I’m suggesting that I feel your (not calling you out, but lots of others, too) opinion of his political identity is influencing your opinion of him as a programmer, which I don’t think is right.
Now, if you had just said ESR is well regarded as a blowhard, I’d do nothing but nod in agreement.
I think some of the criticism of ESR comes from the fact that he touts himself as a master programmer (IMHO he takes hubris to new levels) when his code suggests the opposite.
I often go entire months per project without committing a bug to the repository. There have been good stretches on NTPsec in which my error rate was down around one introduced bug per quarter while I was coding at apparently breakneck speed. This is how I do that.
And earlier in that article:
Yes, there was a bug in my vint64 encapsulation commit. I will neither confirm nor deny any conjecture that I left it in there deliberately to see who would be sharp enough to spot it.
ESR is someone and I am no one but this deserves a response.
As someone else mentioned, the two complaints here fall into (a) Rust is severely hard to use and (b) there is very poor documentation.
The learning curve is high. I couldn’t compile anything for a day and a half when I started learning it. But when it clicked, it clicked and it was smooth sailing in an extremely pleasant language. The ownership model is only as hard as C++ RAII, so if you know that, you’re fine. You just add the fact that pointers can’t live past the thing they point to. And it’s substantially simpler than C++ for what it accomplishes. In fact I don’t know how you could write a language any simpler than Rust’s with the same performance and safety characteristics and abstractive capacity as Rust’s… Perhaps D is a good competitor. But I also don’t know everything so I can only speak to what I’ve seen perdonally.
I don’t know what Eric means by a strange ritual for concatenating two strings, but I’ll give him the benefit of the doubt that it must have seemed unusual and unnecessarily obtuse. But I also think that if you’re new to an opinionated language, even if you’re a famous UNIX programmer, everything seems strange. Personally, I just look at the documentation for Rust’s String, and see there’s a method “push_str” that takes a pointer to the String and a string slice (what amounts to a pointer to another string). That seems identical to the type signature of C’s strcat! There’s also an overload of the + operator that does the same thing.
I find the documentation is superb. I think how good the documentation is partially explains why Rust has become really successful in terms of getting reasonably popular as fast as it did. The “book” (it is much shorter than a book and is organized into topics) explains everything I need to know about the language. Any holes in my understanding are filled immediately by helpful (and polite!) community on IRC or Reddit.
Rust is a year and a half old since 1.0. I say that not as an excuse, but as an accomplishment - what a language (and ecosystem, and set of tools, and documentation, and…) for such a short time since stabilization! This comes with the caveat that some things that seem like glaring holes might actually be very well implemented outside of the standard library… by people who work on the language itself! For instance Alex Chricton and others have just recently released Tokio 0.1 (the “one-stop shop for asynchronous code”), and he’s on the Core team, the Libraries team, AND the Tooling and Infrastructure team. This is not third party, this is allowing for rapid iteration and development of new parts of the language’s standard lib before having to make the real, non trivial commitment to maintain it forever by putting it in Std.
Go has also had 7 years since its version 1 release. That doesn’t meant comparisons are unreasonable but it does mean that a little nuance and benefit of the doubt should be given to Rust during a criticism.
And by the way: the design decisions Rust has made has given it the ability to provide significantly more throughput than Go can achieve, all without a garbage collector. That is a good counterpoint to any comment about Rust not having green threads.
An issue I had with regards to documentation is that sometimes I find myself trying to write a thing with (for example) Vectors in “C style”, running into ownership issues, and then looking at the Vector docs.
And not really finding which of the 40 trait implementations let me do a variant of what I want.
I think a “Rust idioms for C programmers” would be excellent and helpful. “Here’s things that you can do in other languages, not in rust, but here’s how to do it in Rust”
For example, how do you read in a number N, then a list of N strings (newline separated), and have those all be in an array? Or rather, what’s the most idiomatic way to do so (that steers clear of un-necessary
mut
s or copies)I’m glad you’re sharing your experience because anecdotes like these improve things for everyone, as well as provide a corpus for people to get predigested answers.
What kind of ownership issues do you typically encounter with vectors?
There are a lot of traits. The most important ones all get a mention in the book, but sometimes it is annoying to look at the docs for a type and wonder if the behavior you want is hidden in a trait listed below.
What sort of idioms do you mean? In my view it seems easier to just start with teaching a Rust perspective and then answering the questions that arise that way, because they’ll be more idiomatic questions than asking people to convert concepts or techniques. But I see the value in a Rosetta stone.
Here is a minimal example of reading an integer and then reading that many strings into a buffer.
I don’t think that rtpg’s specific problem is there, but one way to help in that regard is to check the rosettacode list of problems without a Rust implementation and contribute.
Rosettacode is certainly not the perfect forum, but it’s a pretty good attempt to solve the idiomatic language translation problem.
“In fact I don’t know how you could write a language any simpler than Rust’s with the same performance and safety characteristics and abstractive capacity as Rust’s”
Try Cyclone, Modula-3, or a subset of D with the borrow checker added to any with a GC. Cyclone was one of Rust’s predecessors aimed at C developers. Modula-3 was my favorite alternative to C++ given safety & compile speed. As a Wirth-style language, it has almost no learning curve. The D proponents in Rust discussions usually parry what features Rust people bring up. All that tells me is D does a lot but with syntax & design aiming for C or C++ programmers more. Should reduce learning curve.
https://en.wikipedia.org/wiki/Cyclone_(programming_language)
https://en.wikipedia.org/wiki/Modula-3
https://en.wikipedia.org/wiki/D_(programming_language)
Has anyone thought through what Modula-3/D looks like with a borrow checker? IIRC, Cyclone doesn’t have lifetime annotations. Since I don’t know any of these languages, it’s hard to say what they would look like, but at least in Rust’s case, the complete absence of lifetime annotations would hurt abstraction power pretty significantly.
Cyclone’s region-based memory management & special pointers were a partial inspiration to Rust’s scheme. The modifications probably wouldn’t take much outside coding style. Modula-3 with a borrow checker would probably just be much simpler than Rust. That’s more my point. D I have no idea about except to say it’s a safe, expressive language that I’ve never seen people gripe about like Rust’s learning curve. Code samples I looked at look comprehensible since I used C & C++.
Type systems has the annoying property that “just adding one more feature” tends to invalidate the metatheoretical properties (such as type safety, or the decidability of type checking and inference) that are the entire justification for using a type system in the first place. It’s a mistake to think that you can retroactively add features to a type system without suffering negative consequences. When Java added generics, the design turned out much uglier than it would have been if generics had been a language feature right from the start.
Another (complementary, rather than competing) approach to writing safe programs that I feel hasn’t been given sufficient attention, is to use a language with an axiomatic semantics. In such a language, it isn’t the language implementor’s job to reject meaningless programs. Rather, it is the language user’s job to make sure the program he submits has a meaning. And the meaning of a program is whatever you can prove using the language’s axioms and nothing more, so there’s no need, unlike the C and C++ specifications, to say “such and such has undefined behavior”.
I think my point is that I’m skeptical of the idea of “just bolting on a borrow checker.” It’s a significant addition and it’s not clear what else in the language under consideration would have to change. I just simply don’t believe that you can say, “simpler language + borrow checker = simpler than Rust” so easily. :-)
I’ve worried about this possibility before. You may be right. Maybe I’m just being optimistic that a simple, safe, and imperative language would at least make it easier. Who knows until it’s actually attempted, though.
Cyclone’s region system was, in fact, the direct ancestor of Rust’s lifetime annotations.
I realize that. I thought there was something that Rust had to add that Cyclone didn’t have, and I thought that something was lifetime annotations. But I could be misremembering. I don’t know Cyclone well enough.
I did mention that D was a good competitor in this regard. Modula-3 severely restricts the user in a number of ways like being unable to pass pointers to stack variables, which is critical for zero-copy and non-allocating code, as well as doing plenty of runtime checks and providing a garbage collector.
D is a great language, but the last time I checked it still couldn’t generate a runtimeless library that could be called from C. if it had that and basic algebraic datatypes with pattern matching I’d start using it immediately.
I’m not fluent in Rust, but I think the author forgets that C itself has a really high learning curve.
I remember learning C. It was hard. To use one the author’s own examples: Would you say that concatenating strings in C is a breeze? I wouldn’t. Should I use a statically bounded buffer with
strcat()
,strlcat()
orstrncat()
, but then what if the result was too long? Or I could use a heap allocated buffer, but then I need to remember to free it again, and if I don’t the language won’t tell me and I have a leak. If I get it really wrong then I corrupt memory (possibly) resulting in a crash. Joining two strings in C is far fromstr1 + str2
as it is in many languages.Are these rituals not in-place to prevent you from making mistakes like the ones I mentioned above in the string concatenation example?
You can poll sockets using the mio crate, it’s just not part of the core language (yet).
Overall, from this article I get an air of “I don’t like it because I am unfamiliar with it, and I have unrealistic expectations of a systems programming language that isn’t C”.
Also see Stefano’s comment showing that string concatenation is actually not hard in rust:
[Comment removed by author]
I think that this is largely why the author is planning on ditching C and evaluating alternatives.
I’ve spent a couple years writing most of my code with rust now, and I’m still being smacked by the learning curve on a regular basis. I haven’t found a way to dive deep and then be able to blast code out without a war with the compiler, like you can do with scala with a thought-dense book like Functional Programming in Scala (I felt like I was constantly smashing my head into a wall for a year with scala until I read this). I keep going with rust because I feel like I’m cultivating future opportunities to contribute to something that replaces widely deployed infrastructure with something that takes the keys (exploits) away from governments and organized crime, which is quite motivating to me.
I admit to not being a much of an ESR fan, but this actually seems like a fairly reasonable post.
I gave rust a try a while ago (shortly after 1.0 shipped, as I recall), and at the time, I didn’t find it a very pleasant fit for the kinds of things I wanted to do (network services (http, tcp, udp)). Maybe I just didn’t “get it” or “didn’t give it enough time”. Not sure.
I currently use Go (or python for the occasional rest api), and find it “pretty ok”.
Makes me wonder what languages I will be using over the course of the next 5 years though.
Server-side Swift? Will D finally break out? Crystal? Elixir? Nim? Pony? Myrddin?
I’m seeing a TON of activity in Elixir. Its Ruby-like syntax and real erlang derived MP model seem like a really solid value prop with a relatively shallow learning curve.
The question is what systems programming language will we be using. There’s a ton of comfortable high level languages, but by contrast there are only a handful of systems programming languages. It’s 2017, and I think people are growing tired of buffer overflows, and are super keen to wave manual memory management goodbye. But then GC doesn’t suit systems programming either. This is the “niche” rust is trying to address, but we have yet to see if the learning curve will be its demise.
I’m looking forward to a time when the phrase “systems programming” doesn’t implicitly throw out the idea of a garbage collector. I’d personally like it if Go was a little bit more strict about stuff like checking for err, but if someone said “Hey, we’re going to write an operating system top to bottom in Go” I’d be all for it. Whatever inflection point we needed between user expectations and hardware limitations that called for no garbage collection we went past a long time ago.
I could have maybe entertained the idea that my phone would need it, but my phone has such horsepower now that manually managing object lifecycles is a much smaller issue than just straight up business logic bugs.
Maybe the network stack for a real time online video game. But if we’re doing that, we don’t need a brand-new language to do it; safety isn’t the concern there.
Are you sure about that? I don’t doubt what you’re saying, but I’m hearing and seeing an awful lot of movement around go even in contexts that would have been previously considered to be “systems programming”.
I agree there will always be a niche for non gc languages that need max to the metal performance, but how big that niche is has yet to be determined IMO.
As to Rust, I think most people agree there’s an enormous amount of untapped potential there. It’s unfortunate that some large company with associated large $$ and man hours doesn’t back the project. I think that would help smooth some of the rough edges and make the adoption experience a bit smoother.
Elixir has a shallow learning curve. OTP (the main benefit of BEAM languages) doesn’t, especially for web developers used to Ruby and Node. It’s not “hard” or impossible, but like Rust, it’s not Python.
Good point about the OTP aspects.
I’d be interested to learn what the performance characteristics of non OTP Elixir are versus similar code running in Ruby or Python.
Anecdotally, I’d say Elixir feels about 2-3x faster than Ruby or Python in the single-threaded case. But just about any Elixir app will use multiple processes (Erlang processes, not OS processes), and at that point the benefits of concurrency quickly leave Ruby and Python in the dust.
EDIT: A couple of benchmarks say roughly the same thing I do:
I think raw Elixir is faster, but the problem is that every real world library or use case of Elixir will use OTP in some way or another (at the very least, a
GenServer
, which is admittedly very easy to learn).I think ESR is right and wrong here. Right because Rust isn’t the right tool for what he’s trying to do. Wrong because Go isn’t a C replacement, and he never needed C in the first place for what he’s doing.
I think Rust is a great language–I use it for developing the runtime system of a research PL I’m working on–but the community oversells the value of GC-less operation. Most of the “complexity” of Rust stems from having the type system track references. This bugs me because for many people Rust will be their first introduction to a “good” type system, and I don’t want people to get the wrong idea that typeful programming is all pain little gain.
[Comment removed by author]
I don’t think there is anything in particular about an NTP server that requires a GC-less language. Rust sans borrow checker would probably be a fine, easy-to-use choice. I thought about unpredictable GC pauses interfering with synchronization but apparently Go works for him so that can’t be an issue.
I don’t think writing an OS kernel and a server have much in common. I think Rust is a fine language for developing a kernel in. If by “OS” you meant all of the supplemental stuff that isn’t running in kernel mode, then I agree, but then again I don’t think C is an appropriate language for it either
[Comment removed by author]
There’s nothing special about Go’s language design wrt GC, and its implementation uses a standard collector aggressively optimized for latency at the expense of throughput. The same principles apply to any other language, whether or not existing languages make this trade off in their collectors is irrelevant to my point. The way you word this sounds like it is intended to be a rebuttal, but I see it as supporting my claim that GCed languages are quite appropriate for this domain.
Maybe? I’m not particularly interested in spreading the gospel of Rust.
I would use Rust for anything I would use C for.
Embedded programming, where you need tight control over allocation
OS kernels & drivers
Language runtimes (I include Servo in this)
Libraries that need to make minimal assumptions about their runtime environment so they can be used from any language
Games, and more generally anything A/V. The people I know in game dev tell me that using C# (Unity, etc) is a pain and they have to be very careful about controlling allocation rates
Is this research PL public? what about the implementation?
I’m working on a language looking at mutation and aliasing control with some similar ideas to Rust, but focusing on a higher level such that I can afford the overhead of a GC.
I think a similar language with a good gc would be a better fit for most use cases.
As a mild aside: I also think Rust deals with internal mutability poorly (it is invisible to the user of a potentially internally mutable object).
Rust community folks (@steveklabnik et al.), please use your mailing lists or whatever to get your street preachers on the same page about not sounding like jerks or fanatics:
To be fair, there’s a lot of really disgusting nonsense in the other direction, but this sort of posting is why a lot of folks view the language claims with skepticism.
If you’re going to quote a negative comment, you should also consider linking to any one of the numerous constructive comments by Brian Campbell.
Despite what you may think, we don’t actually believe we can eliminate all fanatics everywhere. It seems weird to hold us culpable for that. We do our best on home turf, but no, we don’t moderate ESR’s blog.
I don’t think you get it–for humans, negative experiences are going to outweighs positive ones every time.
Every time somebody says “I have these problems with Rust” and more than none of the replies is “Rust is great works on my machine git gud” you all look like assholes.
See also the massive and pervasive success of Haskell.
I get it quite well. I understand the power of perception. I’m not talking about humans. I’m talking about one very specific instance: your comment. I mean, really, you’re going to claim that people are skeptical about Rust because of inflammatory comments made on ESR’s blog, of all places? It defies credulity!
You’re also insinuating that Rust moderators and community leaders are somehow responsible for every little thing uttered by random people on random blogs. This isn’t about me denying human perception, this is about me calling out your nonsense. Street preachers? Really? Give me a break.
Look, I get that I’m saying negative things about your tribe, and so of course you’re going to be less-than-charitable in your reading of anything I say. This would also be a great time to wear your hat, to at least make it obvious to everyone that this is the case.
No, I’m not going to make that claim. I’m sorry if that’s how my wording came across–what I meant to point out was that this was a glaring example of the sort of behavior that is pervasive on places like here, HN, and various other programming communities.
For the reason listed above, I understand why you’d like to interpret my complaint in the narrowest-possible (and hence easily dismissable) context, but that’s really not what I mean and I suspect you know it.
Ruby has this great saying “Matz is nice and so we are nice”. Other communities have had similar results. Some communities (Linux) have leadership that set the tone for acceptable discourse. So, it’s totally possible to have leaders set standards and lead by example–and I refuse to believe that the Rust folks (especially given that a few built their careers on being outspoken) couldn’t somehow do a similar thing.
I believe the analogy is accurate. You aren’t paying them, they do their missionary work in public, and sometimes they’re annoying.
Again, I don’t hate Rust or anything–I just prefer seeing more useful descriptions of why it’s awesome and concrete examples of it being used.
I had a longer comment written out, but I thought better of it.
I wear my hat when I’m speaking in my capacity as a member of the libs team. I’m not speaking in that capacity with you. I’m not putting on my hat every time I mention Rust.
If you’re allowed to say that you want more useful descriptions, then I’m allowed to say that I want fewer inflammatory analogies from you.
fwiw, this is hotly debated in the community as a bad thing, one person calling it the most harmful thing in Ruby.
(Not to sidetrack, but context seems important.)
steve and everyone else who moderate and manage the rust subreddit, forums, and irc channels do a wonderful job disallowing negative and zealous commentary while fostering a productive and respectful space for discussion. I have had a very pleasant experience in all the rust spaces and can assure you the message is already out there.
[Comment removed by author]
For me the appeal to rust would be ML style but fast, but Rust feels a hair too far from ML languages for me to get really into it. The most egregious example for me is Currying/Partial Application in rust. It feels like I can get close to how I program but not quite there. ESR I imagine is much more the C/C++ programmer who doesn’t get why there’s all this functional nonsense in his new language. Of course the real reason is, it’s great, and make a lot of stuff simpler. I’ll keep an eye on rust as time goes on, once it gets proper partial application I’ll give it a shot.
This is unfortunate… I’m thinking that subsetting C++ with a new compiler might be the way of the future. Bjarne is working on that [1], and maybe there is some overlap with the boringcc idea [2]:
I feel that such a project would be an order of magnitude less effort and have better adoption characteristics than Rust. It boils down to writing a naive C++ or C parser and code generator. You don’t have to make it fast because the goal is to make it secure.
And then generate object files which can be linked into objects compiled with LLVM, so you can interoperate.
I appreciate Rust’s effort, but unfortunately I think history has taught us that languages are adopted because of what they can do, not because of what they can’t. All the warts in C and JavaScript opened a lot of doors to “creativity” by outsiders, like metaprogramming with the C preprocessor and libraries like jQuery.
C and JavaScript are “by default you can do it”… Rust is “by default you can’t do it”, which means there are huge holes in the ecosystem.
And honestly I think that “rewrite in Rust” is one of the worst strategies for making software secure that I can think of. It is crazily expensive and only rules out a subset of bugs. From what I can tell, ESR understood this and spent a long time cleaning up the existing C codebase.
It’s kind of a “run before you can walk” thing… if you have tons of dead code from 1995 or 1985 lying around, I’m not sure what makes you think that you will have time to rewrite in Rust, when you haven’t even done basic hygiene on your project.
Although to be fair, the Corrode Rust-to-C translator is a counter-argument. But I bet it would be a lot simpler if Rust was designed from the start to auto-convert from C, rather than designing the language first, then tacking on the converter.
I’ve found with my shell project [3] that adhering to compatibility can actually improve the language design. It imposes constraints and lets you learn concrete lessons from existing code.
(Also, I have to point out that although I enjoy ESR’s writings, he has insanely poisonous people on his blog…)
[1] https://github.com/isocpp/CppCoreGuidelines
[2] https://groups.google.com/forum/m/#!msg/boring-crypto/48qa1kWignU/o8GGp2K1DAAJ
[3] http://www.oilshell.org/blog/
Has Stroustrup or anyone else working on the C++ Core Guidelines made any official statement about the kinds of misbehaviors that they rule out? If so, is there a proof sketch that the statement actually holds? Heck, is there even circumstantial evidence that the statement is plausible? If not, what makes these guidelines any better than the gazillion unsound static analyses that already exist for C++?
Experience tells us that we mustn’t blindly accept anyone’s word regarding a programming language’s metatheoretical properties (like type safety). Even the most competent and well-intentioned language designers make mistakes. Here are some examples among languages intended to be type-safe:
I have no idea about “adoption characteristics”, which are ultimatley driven for social reasons. But, from a purely technical standpoint, I’m pretty confident it’s at least one or two orders of magnitude easier to write a resource-correct program in Rust than an equivalent one in C++. It’s difficult to compete with “errors are located and traced back to their exact source”.
Ruling out nonsensical runtime behaviors is most certainly something a language can do, whether you appreciate it or not.
I think you are missing my point, which is precisely about adoption characteristics. I’m claiming that Rust’s design goals are laudable but will cause it not to be adopted.
Pascal ruled out many more undefined behaviors than C. But Pascal lost and C won, precisely for this reason. Sometimes you need to do weird stuff. People underestimate the diversity of use cases programming.
If Rust’s goal is to anticipate all use cases, then it will fail, because nobody or even no one group can do that.
ESR mentioned something about being able to select over only a fixed number of channels? That reminds me of Pascal’s choice of fixed length strings. It makes it easier for the compiler to enforce certain things, but harder for the programmer to get his or her work done.
I presume if you have fixed length strings then you never have buffer overruns. But that is not the right solution to the problem – programs need dynamically sized strings!
Although I disagree with a lot of what Jon Blow says, look at his Jai programming language. He talks about “80% solutions”, and the fact that the cure could be worse than the disease. The problem is that Rust has 100% guarantees in some places, and then 0% in other places. I believe that a more balanced design will have 80% solutions and cover a wider variety of use cases.
Also see “Rust skipped leg day”.
I’m not sure that’s a great example, since channel selection on
std::sync
channels isn’t even available on stable Rust. Moreover, this is a library quirk rather than something that is inherently limited by the language.What’s interesting is that Go doesn’t support selecting on an arbitrary number of channels either. You need to write
select
with a fixed number of cases that can’t vary at runtime. (OK, so this is a lie, you can actually use packagereflect
to build aselect
at runtime with an arbitrary number of channel selections.) Why was this glossed over? I don’t know. shrugsOne of the key things missing from this blog post is due diligence on the state of async IO in Rust, since that clearly seems to be what he was chasing based on him wanting an
epoll
interface. The blog post links to an issue that is over two years old as the definitive last word. Now, to be fair, while I think the OP did not do their due diligence, that doesn’t mean the Rust ecosystem doesn’t share any of the blame either. We need to make this stuff more discoverable. We will, but it’s still early days. On top of all that, there is literally nothing stopping anyone from usingepoll
directly just like you would in C. So I think this stuff about Rust “preventing” you from doing things is pretty misguided.My main point is this: I wouldn’t read too much into ESR’s blog post. There’s definitely something to be learned from it, but “Rust is going to fail because string concatenation is hard” or “Rust is going to fail because
std::sync::mpsc
doesn’t support dynamic channel selection” ain’t it.I don’t think that’s true. If someone hasn’t built a safe interface around some C library, then someone can just go out and build it themselves. Rust gives you the abstraction power to bundle an unsafe core into a safe interface.
Pascal is in many ways a more disciplined language than C, and that’s a good thing, but it doesn’t really rule out any unwanted runtime behaviors that C doesn’t.
It isn’t. There exist use cases for which Rust is woefully inadequate, and that’s fine, no language has to be good at everything. However, there’s no use case where data races and uses-after-free make sense. Or maybe I just lack imagination.
This is a standard library problem, not a language problem.
It’s fine to criticize those 0% guarantees in other places, e.g., the lack of integer generics really hurts. But why would you want to reject 100% guarantees whenever they’re available?
Addition:
For the record, this isn’t true. Getting the 20th character of the string “Hello, world!” is wrong, whether the string is statically or dynamically allocated. Statically allocated strings merely make it easier to detect potential buffer overruns, but by itself doesn’t rule them out.
Because language design is about tradeoffs. 100% guarantees don’t come for free – they hurt the experience in other areas. Language features can’t be considered in isolation.
Another good example is Swift’s powerful type system causing exponential blowup of algorithms in the compiler: https://www.cocoawithlove.com/blog/2016/07/12/type-checker-issues.html
I believe this is more like a mathematical theorem than an issue of engineering.
Elm has a very strong type system too, and as far as I can tell, it is hard to express basic things with dictionaries:
http://reasonablypolymorphic.com/blog/elm-is-wrong
“Experience” is a subjective psychological issue that I shall not address. As Dijkstra said, we must separate the issues of correctness (relative to a specification) and pleasantness (relative to the user’s whims).
But, yes, 100% guarantees in programming languages don’t come for free. They have to be proven, just like any other theorem. And proving theorems is hard work.
That greatly depends on how orthogonal your feature set is. Not all languages are like C++, where everything interacts with everything else in who knows how many ways.
I’ve seen worse. You can easily send any sufficiently fancy type checker (GHC Haskell, Scala, Rust, C++, etc.) into an infinite loop without even trying. Even Hindley-Milner type inference, which is as simple as it gets, runs in exponential time in the worst case, as the following program shows:
But the real problem, in any case, is how to come up with program designs that don’t requite too much effort to verify (not necessarily using types).
If you actually read that article, the author is bemoaning the lack of type classes. (And by “dictionary”, he actually means what a C++ programmer would call “vtable”.) He’s not trying to work around safety measures in Elm’s design.
Swift makes a special effort to resolve literals to types conveniently. This is not an issue with the power of the type system – which relates to what is expressible – or the functionality of the type checker – which relates to what is checkable – but rather to overloading literals in a fairly ambitious way.
Many typed languages will infer that
let i = 1
implies thati
is an integer or number of some kind; and ditto with strings. Typically with numbers there is a little juggling to determine which kind of numberi
is – like if it is anUInt32
or anInt8
– depending on how it is used later in the program. With strings you can do the same thing. A literal likelet url = "https://web.com"
could be inferred to be aURL
and not aString
if elsewhere in the program it is used as one.Swift tries to do this for structured literals – arrays and dictionaries – which is pretty tough since for every literal in the literal you have some branching. In most languages you just couldn’t even do that – have it infer the type of your dictionary for you – you’d have to write the annotation. But this is a case of a feature other languages just don’t have, not something that is particularly awkward or slow in Swift.
Say what you will about ESR, but the guy generally isn’t regarded as ignorant when it comes to programming.
My key take aways from this are the following:
select(2)
or epoll support is much more superior to a language which outsources it to, even if well done, an external repository. The reasons for this being the lack of clear support into the future (10 year plan), and lack of clear choice amongst a swath of similar libraries.I must say, I have experienced frustration with 1 in languages other than Rust. OCaml and Lua both suffer from this, for sure. And, even with Rust, a couple of months ago, there wasn’t a clear HTTP client I should be using. I didn’t pursue a project because of (my perceived) friction – I used Go instead.
I haven’t, however, found 2 to be true. Now, I won’t claim that Rust is trivial to pick up, at least not in comparison to someone with a C background picking up Go. But, I certainly didn’t have trouble building a few small >100 line toys after a little bit of reading, admittedly some hands on frustration, and compiler error searches. Luckily, all of the compiler error searches I encountered were extremely easy to search for, and it cleared everything up quite quickly.
All this being said, I want to adopt Rust over Go, because I prefer the philosophy, but between work, which is a large Go code base, and home projects (a mix of Python, Racket, C, Go), I simply haven’t found the motivation to dive into it. Maybe there is actually something to
2
…Wait, why do you say he’s not generally regarded as ignorant in programming? His primary technological reputation comes from text files he took over updates for, and essays on culture. His code is not good.
https://twitter.com/tqbf/status/777723458964684801
[Comment removed by author]
Well wait, then why do you have a strong opinion on the subject? Why in fact would you have any opinion at all?
The existence of the fact that programmer maturity is possible in three decades does not mean it happened. I’ve looked at the code. He attempted to reimplement the 64 bit integer data type by hand in ntpsec and failed. I have junior devs working for me who, if 64 bit integer types weren’t already a standard on every platform for the last 17 years, would create bug-free (re-)implementations every time. In fact, I can’t remember having a dev working for me in the past 26 years that would struggle with it. Of course, every dev in the last 17 years would look at me like I was an idiot – which is the other part of the problem.
[Comment removed by author]
Or fetchmail, or…
Oh you mean fetchmail that was used successfully by thousands of people, and was basically the only choice for years?
fetchmail (which was initially developed by Carl Harris) is not an example of good coding or good design - in fact, it’s quite the opposite. It can quite easily lose mail in cases where it shouldn’t.
See, for example some of the comments on the getmail FAQ. Yes, I know that getmail is a “competitor”, but those are fairly damning. And don’t forget this - “As to fetchmail: it is an abomination before God”.
Matthew Garrett (who I know has his own axe to grind with ESR), has posted some negative comments about the quality of his software. See this tweet for example.
I do! The man writes usable code. I have no opinion on how elegant it is, but it’s functional.
I think it’s important to separate the person and their opinions from their work. esr’s work speaks with a loud clear voice, even other aspects of his persona do not :)
I agree! Sorry, I misread your point about fetchmail. I’ve corrected by upvoting it. :)
I have some philosophical issues with some of his edicts (particularly ‘silence is golden’ which I think is the biggest train wreck EVER in just about every way I can think of) but you can’t deny that he’s produced several bodies of code used by a heck of a lot of people.
Can’t honestly say I’m his biggest fan, but credit where credit is due.
The Rust community looks to be settling on https://crates.io/crates/tokio for the select/epoll use case. His other argument is a little more understandable. And there is no denying that for the task of writing a simple IRC server Go is going to get you up and running faster than Rust. However for something like NTPSec I’d argue that the extra safety Rust gives you is worth the tradeoff on ramp up speed.
See, I’m inherently untrusting of anything that follows that statement, categorically.
I remember getting the first edition of the Programming Elixir book by Dave Thomas, where he confidently asserted “the community this” and “the community that” on patterns that were pretty obviously not set in stone or were personal style choices.
It’s waaay too easy for a few bozos who troll message boards to pretend to speak for the entire community (and no, the irony is not lost on me while making that statement). Especially having been burned by the JS ecosystem where “The Community” was 300% behind Backbone as the way to^W^W^W^WKnockout as the^W^W^WAnguler^WMithril^WReact as the way to do things.
Like, we’ve got to do better than mob-driven-development, because the reputation of “The Community” as a thought-leader is (or at least should be) thoroughly tarnished.
There’s nothing interesting about the phrase “the Rust community …” It’s a description of reality. It’s saying, “I’m linking you to this resource, and this resource has significant buy in from a lot of people.” People value that kind of information. If you don’t, then just say that.
I think it’s worth recognizing that particular phrases are frequently abused for rhetorical purposes - usually because there’s some ambiguity in the phrase itself. And in my experience this happens a lot with “the X community” - people frequently say “the X community Y” when they’ve only actually spoken to a couple of X, and from their perspective they’re not lying, but it can all too often be the case that most X don’t actually Y. (Indeed the implicit assertion that there’s a single cohesive X community is, in my experience, almost always wrong). I’d put a lot more credence in a specific statement like “Z out of W of developers surveyed at RustConfUS listed toiko as their preferred async solution” - or even just something like “I’ve got 20 Rust projects starred on GitHub and 5 of them are using tokio” gives a much clearer picture of what “Rust community” they’re talking about.
That’s reasonable. The particular community I was referring to is mostly the Rust core dev team and the mozilla rust irc channels.
I’m interested in the Elixir patterns from the Dave Thomas book that are currently not common Elixir patterns. I skimmed the book, but I don’t remember anything strange or now considered bad practice. Elixir has had widely-used conventions from the start.
What extra safety does rust give that Go doesn’t?
Safe Rust cannot cause data races, or uses-after-free for several kinds of resources (not just memory). To be precise, safe Rust can have these problems, but they will ultimately be traceable back to unsafe Rust code (or language implementation errors).
Exactly. The Rust type system allows you to give much stronger guarantees that are compile time checked than rust will give you.
I like both languages. Go is very much a frictionless language for me. I feel very fast when I’m writing with it. But if I’m writing software who’s purpose is to offer a strong security guarantee I’d look at Rust first.
Go doesn’t force you to handle
nil
, for one.Rust has a much more expressive type system which reduces the amount of copy and pasting, dynamic reflection, and ad-hoc code generation you have to do. It forces you to handle error cases (via
Result
). Nonil
(via.Option
). No data races thanks to the borrow checker.I found this with Lua as well. The native’s attitude tends to be “But it’s C-like!”. Not helpful.
But he is regarded as ignorant when it comes to programming…
I think there is a lot of evidence that suggests otherwise. His book The Art of Unix Programming is rated 4.1/5 on amazon. His (accepted) contributions to things like GNU Emacs, Python, as well as a number of tools such as gpsd, fetchmail, reposurgeon, etc, etc suggests otherwise. Now, it’s easy to say, “so? The code isn’t any good!” and that’s fair, but a bit misguided. We live in a world where programmers of languages with no protections against errors, unsurprisingly, make errors and choose abstractions that we later find problems with. When that happens to you, I don’t call you ignorant, point, and then laugh at you.
I don’t mean to defend ESR, because, frankly, someone will assume I support his politics, which I don’t.
But, I am defending the idea that we need to find a way to separate a person’s identities sometimes to create unbiased opinions on an individual one (identity).
I’m suggesting that I feel your (not calling you out, but lots of others, too) opinion of his political identity is influencing your opinion of him as a programmer, which I don’t think is right.
Now, if you had just said ESR is well regarded as a blowhard, I’d do nothing but nod in agreement.
I think some of the criticism of ESR comes from the fact that he touts himself as a master programmer (IMHO he takes hubris to new levels) when his code suggests the opposite.
I don’t even know what to say about this:
And earlier in that article:
(the above stolen from (felixgallo’s)[https://lobste.rs/u/felixgallo) earlier link.
As to the quality of his code, see my earlier comment.