What is Rust and why is it so popular?
stackoverflow.blog
stackoverflow.blog
Honestly, I like it, and for all the non-hyped reasons.
Like ownership. Lots of us in the embedded space aren't so impressed by the extra memory safety because, if you allocate all your memory statically, there are fewer ways to screw things up with references, and it's a lot easier to do bounds checking, so fewer bugs slip in. Not that "even fewer" isn't good, it's just that the impact is less dramatic than in application code.
But: the borrow checker still pesters me about something I've written every once in a while. And every once in a while, what it pesters me about is a data race that would have definitely bit me in the back sooner or later. It has no security implications, I guess, and it wouldn't crash my program, but it's still wrong. Yay!
Or like traits, which are just so refreshingly sane.
Lots of people are hung up on security and segfaults, and spend far less time talking about a far less categorical property: the conventions that Rust enforces make it easier to write correct code. Sometimes it's not straightforward code, mind you, but "works right" trumps "fits in a single line" in my book...
In many ways, Rust is what C++ could have been if it had not been designed by saying yes to everything. I guess it's not there yet, but there's a chance it might be.
On a more, uh, philosophical note, I think it's pretty cool that our industry has been granted a second chance in this regard. We've already been given Ada, and we missed the chance to adopt it on a wide enough scale to make a difference. Maybe we won't miss this chance.
From mostly an outsiders' perspective, I just don't think C is a very nice programming language. It looks easy to to pick up and it probably is (for some definitions of "pick up"), but I don't think that's a good thing. The language gives you almost nothing. Pointers, structs, arrays, control flow. Not much else. I'm barely exaggerating when I say that all the C code I see looks like a chaotic pointer juggle fest for precisely that reason. The few features and library that it does have come with non-obvious gotchas that will punish you later.
Rust is probably much harder to "pick up", and way more complicated in general, but it's just, uh, more language. I'll take pattern matching, encouraging immutability, functional style programming, trait programming over C's simplicity any day of the week. The safety is extremely nice, but I think Rust's biggest eye-opener for me is that all these "higher level" features are just possible at (close to) the performance of C. That makes it very appealing to me.
I think that's part of the problem :) Because Rust looks high level it appeals to people used to high-level programming. But once you get into systems programming, the issues are different from high-level programming, and you're less happy to pay for features that solve other problems. What I'm saying is, obviously, a matter of taste, and some people doing low-level programming do prefer C++'s high-level feel, which is also found in Rust. I hope it goes without saying that just programming in Rust -- or C++ -- doesn't mean you're doing systems programming unless you're, say, writing a device driver, a kernel, a GC, a game framework etc.
I think one of the previous comments, and others on other forums have things right when they say that Rust is a better C++. Anything you would consider using C++ for is a good candidate for using Rust for. Rust is not a better C though, it's just a bit too high level for that. There are languages that are aiming to be a better C (Zig is REALLY interesting, but very very new right now), but Rust isn't really one of them.
I could very easily see something like an OS aimed at x86 desktops being written in Rust. Desktop systems are beefy enough and the problem space is complex enough that the extra overhead involved in Rust (similar to C++) makes sense. On the other hand if you're doing some bare metal work on something like an Arduino you just don't have enough there to justify the extra overhead and something very basic like C (or assembly, or Zig once it matures a bit more) is probably a better fit.
If you're doing anything higher level than kernel programming, Rust is just as viable as anything else, at that point it's mostly down to personal preference on whether you can deal with not having conveniences like garbage collection or not.
PTC, Aicas and Gemalto embedded JVMs for industrial automation can run either with a real time OS, or directly bare metal with Java Real Time JSR.
Android has plenty of Java in it, as of Project Treble it is possible to write user space drivers in Java, something that Android Things supported since the beginning.
MicroEJ has a minimal hardware glue in C, with everything else in Java.
At CCC there was a demo about TamaGo, bare metal Go for USB security devices, then there is also Astrobe with Oberon-07, the new Meadow boards run .NET Standard based OS.
Arduino and ESP 32 support Circuit Python, uLisp, MicroScheme and picoOCaml.
So plenty of systems programming examples with GC.
I don't think so because it suffers from the same limitation that all low-level languages have, which is that API clients need to be aware of implementation details. Rust sometimes makes that propagation of leaked information automatically via the type system, but internal implementation details -- how allocated memory is managed, whether a target is a subroutine or a coroutine, whether an argument is passed at runtime or compile-time -- still leak in a viral way. It's just that Rust and C++ can look high-level, but they don't behave like high-level languages. But Rust is great for those low-level programmers who like this high-level style and are willing to pay for it.
Now, for those who get involved enough in it, they might find issues with the language, and that will provide them incentive to get comfortable with C. Or maybe they won't; maybe they'll get turned off at that point, or maybe they'll find making Rust work a better option, or they'll find still another language that is even closer to C, without the holes.
But either way, providing a way to move in that direction without having to immediately accept all the baggage C brings seems like a good thing. It certainly has moved my mindset from "screw embedded unless it's beefy enough I can run Erlang" to "well, maybe; Rust looks interesting". And I figure if I get comfortable enough with looking into the space, I either
A tool that gets more decent developers to even look at embedded sounds like it would have been a good thing to have 5+ years ago, but better late than never.
What you see happening isn't an accident. There are companies that do excellent IoT work, with excellent people. Many companies that don't could afford to excellent engineers, and have access to a terrific pool of candidates, but their management chooses another way and they're perfectly aware of what they're doing.
(Sauce: am embedded developer, has been involved on and odd in the IoT scene since 2011 or so).
If you want higher level abstraction only in some parts of your codebase, you can do that too. It's easy to mix-and-match.
Rust can do more of the things C++ can do than Java can, but there is a very great deal that can be put in a C++ library that is wholly beyond Rust's capacity.
If you have lesser expectations to capture semantics in libraries, or to use such libraries that do, Rust serves. But the gap will only grow wider.
Just out of curiosity, do you have some examples?
Some more subtle would involve template dispatching according to properties of the types operated on.
But just run down the list of features supported in C++, and not in Rust, and think about how they could change how a library interface or implemention would look. Most C++ features are meant for people writing libraries.
Although they do have security issues regarding use-after-free.
1. Rust is surprisingly great for making cross-platform CLI tools and shipping static Linux binaries. Everybody can just install a single executable and they're ready to go. (Go is good at this, too.)
2. Rust is great for anything where I ask, "How big a cluster do we need for this?" I get roughly the same speed as C++, except there are far fewer footguns. And I can add threading support to existing code in an afternoon, and I can expect it to just work.
3. Rust's very new async support is powerful in some very interesting ways, and I have a couple of programs that it has worked beautifully for. (I'm working on an upcoming open source release of one of them.)
4. Rust's algebraic data types (aka "enum" and checked "match") are a joy for any software involving lots of special cases and complex logic. I think every typed, high-level language should steal this feature.
However, I have mixed experience using Rust for business logic. Yes, item (4) above does help with business logic. But business logic changes a lot, and it needs to be accessible to many developers within an organization.
So on my recent team projects, Rust often winds up doing the heavily lifting. But the rapidly-changing business logic will often get deferred to a GCed language. So far, this is working nicely.
The iterators, enums and no Null types lend themselves to interesting solutions.
The crate ecosystem is also developing fast, and there are decent browsable docs and bootstrapping the env and development environment are all easy.
SDL2 support makes it really easy to make rectangle based games like classic pong and experiment with keyboard and controller inputs etc. (never mind further possibilities of 3D etc).
TTY apps and games are also easy to make with the termion crate.
I think it's just kind of worth learning in a way that (for example) Python, C and Haskell are complementary options for learning different ways of thinking about of coding. Rust feels like a different flavour of programming. It's borrow checker and branch checking etc. they really are likely to lead to better programming in other languages too. Understanding ownership and considering the transfer of ownership are valuable.
Rust is without a doubt a (much) "better C++" but I'm not sure a better C++ is necessarily what systems programming needs most.
[1]: Not that writing that code is as easy as in a high-level language, nor does it offer the same level of abstraction as high-level languages, where changing the details of the implementation does not affect the API and its clients, but it looks high level, or "expressive", on the page.
Is there a reason you’d write your own Rust compiler frontend, rather than just letting rustc generate LLVM IR as it does, and then writing your own LLVM arch target? None of the Rust complexity leaks over into the LLVM IR it generates; it looks just like the LLVM IR that e.g. clang generates.
The leakage even triggers performance issues in compilers: https://users.rust-lang.org/t/5-hours-to-compile-macro-what-...
> LLVM is not good at big functions, I have seen this before as well. One of the functions you are generating is almost 100,000 lines of Rust code including tons of internal control flow.
The same would happen if you wrote (or generated) 100,000 lines of C in one function body, and fed it through Clang with -O2.
If you're wondering, there's nothing in rustc that makes it implicitly turn regular code into huge function bodies, either. It's just someone abusing Rust macros to codegen badly, not taking into account how the granularity of units of compilation interacts with the time-complexity of optimization passes.
Macros are part of the language; they aren’t an external code generation tool.
> The same would happen if you wrote (or generated) 100,000 lines of C in one function body
Absolutely, but neither C nor C++ makes it easy to generate such code. Technically doable with C preprocessor, but it’s not easy. C++ templates don’t normally expand into 100k lines functions either, they can easily expand into 100k small functions.
However, it doesn’t happen with idiomatic C or C++ code. C macros are too hard to use for that. C++ templates are designed to expand into many small functions, as opposed to a single huge one.
It was Rust complexity leaking over into the LLVM IR.
...so are Rust macros. The user was using Rust macros very non-idiomatically.
Also, you don't use C macros to write (large amounts of) C. 90% of the world's generated C, by volume, comes from, I would say, one of two programs: autoconf (through m4), or yacc. And both autoconf check stanzas and yacc's inline C support are easy to screw up in ways that result in horrible code that makes compilers barf.
Also, to be clear, you're conflating two things with the phrase "Rust complexity." This thread was originally about the difficulty of implementing a Rust compiler for a toolchain, which therefore makes "Rust's complexity" here refer specifically to the complexity of the static-analysis semantics of Rust that make writing a Rust compiler frontend difficult.
The solution—relying on the existing rustc frontend for the "hard stuff", and just implementing your own compiler backend to target the embedded architecture—is not rebutted by a claim that you can write bad Rust code that makes the optimization passes of certain compilers choke. You wouldn't even be writing optimization passes. You'd be writing a backend. The optimization passes are universal. It's the job of the LLVM developers to ensure that they don't choke on things like this.
And, despite the fact that 100,000-line functions are dumb, it's the LLVM development team's fault that the optimization passes were written in such a way that their time-complexity is superlinear in a way that stalls out when optimizing a function like that. It's not up to the Rust compiler authors to not emit that type of code, or to somehow prevent you from writing it; it's up to the LLVM devs to make their optimizer work, and work efficiently, for any "valid" code. Honestly, if anything, this code—if you could get it—would be a great integration test to submit to LLVM, for them to work towards passing.
The problem here isn't Rust's complexity; it's autogeneration taken to the extremes without concern for its impact on compile time. The fact that Rust provides builtin macro syntax to do this autogeneration is of little importance, since it will happen just as frequently with external build tools in the C/C++ world.
As an embedded consultant, a better C++ is what systems programming needs most not for technical reasons, but social. So many embedded shops are embracing C++ and it's causing all the sorts of problems you might imagine. While it's possible that these shops will tack back towards a lower-level language, my prediction is that would be a much harder sell than something like rust.
Now, note that I am not saying that Zig's approach to safety is definitely better than Rust. Correctness is an extremely complex issue, with many factors, and it's hard to say with any certainty what works and why. My preference for Zig (which is largely hypothetical as Zig doesn't really exist in a meaningful sense yet) is because I personally value language simplicity and short cycles over what some call "expressivity", but as someone who is also very much interested in formal methods and software correctness in general, I am not convinced that Rust's complexity buys you increased correctness over Zig, and it may even be worse. So the tradeoff is simplicity vs. "expressivity", where correctness can go either way.
In fact, Rust approaches most of these particular problems the same way- arithmetic overflow, array bounds checking, stack overflow handling, etc. are all checked at runtime. Rust's language complexity is dedicated to things that Zig simply doesn't check, and that C and C++ have checked only recently.
This goes back to a previous conversation of ours[1]- Rust's complexity is allocated very differently than C++'s. It has many of the same advantages as Zig (if not more) when it comes to understanding and code review. Painting Rust's static analysis and associated language complexity as an alternative to C/C++/Zig-style runtime checks looks like a category error to me.
[1]: https://www.reddit.com/r/programming/comments/eehe6t/a_backw...
Yes. Take a look at some of the checks Zig has.
> Rust's language complexity is dedicated to things that Zig simply doesn't check, and that C and C++ have checked only recently.
Right, but that doesn't mean it leads to more correct programs when this "checking" comes at a cost.
> It has many of the same advantages as Zig (if not more) when it comes to understanding and code review.
That's a matter of taste. Those who like complex languages like, say, C++ or Scala, can certainly prefer them, but I think they're in the minority. Zig is a very simple language, while only C++ and Scala contend to be as complex as Rust or more.
> Painting Rust's static analysis and associated language complexity as an alternative to C/C++/Zig-style runtime checks looks like a category error to me.
If you, like me, don't particularly like complex languages, then the complexity has a cost. What does it buy you? It buys you certain sound guarantees about lack of UB is safe code, which hopefully helps write more correct programs. If you have an alternative to achieving a similar level of correctness with a much simpler language, I would very much prefer that.
I did, to make sure I wasn't missing something. There's nothing new there, and some things are missing. (This is not bad, Zig is still in development, but it's not exactly a new approach either.)
> only C++ and Scala contend to be as complex as Rust or more. ... If you, like me, don't particularly like complex languages, then the complexity has a cost.
This is why I linked our Reddit conversation- Rust is nowhere near as complex as C++, and its particular complexity's costs are not difficulty in reading or code review.
> If you have an alternative to achieving a similar level of correctness with a much simpler language, I would very much prefer that.
But this is exactly my point- I don't believe Zig is such an alternative! Zig and Rust both improve over C and C++'s complexity and readability, leading to some general correctness improvements; so far only Rust does anything different to address memory safety.
And it still goes further than C sanitizers (except the glaring missing piece of use-after-free, which I'm sure will be addressed).
> Rust is nowhere near as complex as C++
It's hard to quantify this, but I think few people would point at any language (in real use) other than Scala, C++ and Ada as being as or more complex than Rust, so it's at the very top. Some people are perfectly OK with that and even prefer such languages. Rust is well beyond my language complexity comfort level (even C# is too complex for my taste, and, as a high-level language it offer much better abstraction than Rust, or any low-level language). So if C++ is at 500 and Rust is at 300, that doesn't mean much if I want to be at 50 or 100.
> Zig and Rust both improve over C and C++'s complexity and readability
I don't see it this way. I find it hard to put C and Zig in the same category as Rust.
> so far only Rust does anything different to address memory safety.
I disagree with that, too. Rust is the only one that addresses memory safety using sound guarantees. If you run tests on a Zig program out of the box you won't have array overflows; that's not what you get in C and C++. Zig's "big idea" or "doing anything different" is how it does partial evaluation with a single construct (comptime) that is drastically simpler than anything I've seen. In other words, as far as simplifying languages with tough requirements for low-level programming, it's rather revolutionary. That's my preferred direction for programming languages.
Build a C program in the right mode (also required for Zig) and you get exactly the same thing.
Arguably this combination is more complex than Rust's. It has no way to constrain a comptime type argument (traits), so you get error messages like C++ templates. It has no way to constrain a function to be comptime-compatible (const fn), so you get more error messages like C++ templates. When you want to combine things, you manage phase ordering and idioms by hand, basically re-implementing Rust-like concepts at every use site.
Sometimes one or two more concepts in the language makes things simpler. To use a subject you've discussed before, it's similar to scoped continuations vs full call/cc.
In Rust, generics, macros, traits, compile-time evaluation are different constructs with drastically different syntax (and generics and value generics are different features, implemented at different times, but I can accept them as a single construct).
> It has no way to constrain a comptime type argument (traits)
It does: introspection, just as Clojure can constrain arguments, only Zig does it at compile time.
> so you get error messages like C++ templates
You don't because in C++ templates are programmed in their own sub-language with SFINAE tricks (at least in the C++ versions I use; they've probably added new ways to do this, as they do). In Zig, it's just Zig computation. You'd get similar compile-time errors as you would get, at runtime, in, say, Clojure or Python.
> When you want to combine things, you manage phase ordering and idioms by hand, basically re-implementing Rust-like concepts at every use site.
You don't need to reimplement things by hand at every use site; you just call a subroutine at compile time.
> Sometimes one or two more concepts in the language makes things simpler.
I agree, but I think that Zig's comptime brings revolutionary simplicity to the very complex subject of partial evaluation which is so important in systems languages. How well it would work in practice remains to be seen, and I may well be disappointed, but I find Zig's approach of simplicity first very appealing. I agree it's a matter of taste.
Again, these are just as much a single feature as Zig comptime. Traits are like comptime types, or else (closer to what Zig does) comptime type variable introspection.
> You'd get similar compile-time errors as you would get, at runtime, in, say, Clojure or Python.
Exactly- you're not writing plain Zig, you're writing a Zig-adjacent dynamically typed language with new ways to do almost everything and different tradeoffs around correctness.
> just call a subroutine at compile time.
Whoever wrote that subroutine did the reimplementation by hand.
I agree but if better C++ is what the PL industry can give me, then I'll take it, thank you very much!
(Frankly at this point I'll take anything that isn't C++...)
It also doesn't seem capable of safe concurrency.
I think this needs to be more specific. Comparing Rust and C, for instance, it seems complexity can be used to improve correctness; the tradeoff is worth it at least some of the time.
It seems to me the best example of a simple correctness-oriented language isn't Zig, it's Ada SPARK, which has been used in various life-critical software systems. [0] SPARK's advantages are due to its simplicity:
* The whole language has been defined formally (zero ambiguity)
* The language is amenable to formal verification
A monstrously complex language like C++ brings many features, but is much harder to reason about formally, and will never have a formal definition.
[0] https://en.wikipedia.org/wiki/SPARK_(programming_language)
Huh? They are apples and oranges.
Is Rust intended to provide "Direct mappings of built-in operations and types to hardware to provide efficient memory use and efficient low-level operations"? That's one of C++' design goals.
If you change the design goals, you get different advantages and disadvantages.
See more about the C++ design goals in this interview:
https://www.electronicdesign.com/technologies/dev-tools/arti...
I'm curious what your reasoning is for saying this?
1. A lot of resource-constrained firmware is so straightforward that Rust's benefits simply don't justify the effort of learning it and using it. In turn, without sufficient demand, there isn't much motivation for large tooling vendors to get into the game. Without the large tooling vendors, the kind of companies whose money could shift the landscape significantly won't get in the game, either.
2. Lots of embedded software development is outsourced. The margins are pretty small, and the companies in this field are both unwilling and unable to build technical expertise early on, so there's a lot of inertia.
3. Lots of embedded software is developed based on manufacturer-supplied SDKs. Manufacturers in this field tend to run high-inertia, cost-conscious software teams that take a long time to catch up with the rest of the software industry.
4. A lot of this inertia is mistaken for prudence ("we value stability", "we don't just jump on the hype train") and touted as a positive thing. That's why IoT security is what it is, for example (and it's not just IoT, and not just security. Lots of areas in the embedded space suffer from terrible practices). This is an industry where an embarrassingly large number of companies are just discovering continuous integration. A whole new programming language won't just happen overnight.
5. Lots of resource-constrained firmware is written with pretty strenuous regulatory constraints in place, under considerable pressure from internal standards, audits and so on. In some regards, Rust's tooling ecosystem still needs to grow and mature. AFAIK there's no serious equivalent to Coverity, for example -- and plenty of companies value it not just for the analysis it makes (which doesn't mean as much as it seems because the Rust compiler does a lot of static analysis on its own), but also for the kind of traceability and integration that it gives them, and the ability to implement the kind of internal processes that look good on paper during an audit.
It's a mix of legitimate and non-legitimate concerns (specifically, #5 is very legitimate -- lots of internal processes are just software safety theater, but many aren't).
Do you think that Rust could instead help outsourcing? Similar to how Java's GC keeps teams away from segfaults, could Rust's safety guarantees give companies more confidence that an outsourced team's code would have fewer critical bugs?
I guess -- but take it with a grain of salt, given what I've said above -- that it's something which shouldn't even play a role in deciding whether to outsource something or not. There are infinitely many ways to introduce critical bugs, and Rust -- any language, in fact -- helps with only a tiny subset of these. It won't help you against misusing crypto primitives, or failing to validate paths, or a bunch of other things.
(Edit: I guess what I'm saying is that, if you don't trust a development team to get something done in a language, you shouldn't trust them to get something done in any language. Bugs can happen in any language, and some languages make it easier than others to introduce some types of bugs. But good developers should be able to write good software in any good language, it's not like we're comparing Rust and INTERCAL).
Also, Rust is pretty complex, and it attempts to solve a lot of non-trivial problems. In my experience, attempts to handle this kind of complexity by organizations that focus on nothing but price and headcount misfire very, very badly.
There is a massive number of devices between an 8-bit PIC and a Cortex-A. Obviously you're not going to use Rust on a tiny PIC or similar, hell that architecture is outwardly hostile towards even C. I'm guessing these very low power chips will exist for many years to come, but a lot of work has moved over to things like Cortex-Ms which absolutely benefit from things like Rust.
ASM is for the use-case where you're optimizing code in terms of using specific instructions or intrinsics; but it's not really for the case where you're concerned with specific opcode encodings (as ASM abstracts that away, grouping multiple different opcode encodings—some short, some very long—behind a single friendly instruction name.) In that case, you have to work with the machine-code directly—or, at least, through data structures that contain machine-code at the base level, and so can tell you exactly what machine-code they contain.
These systems have the resources to run managed runtimes. I’ve shipped embedded software running .NET core 2.2 on ARM Linux (RK3288 with custom set of peripherals), only a minor part of the software being unsafe (a shared library written in C++).
Over the last 20 years, desktop GUI software, web servers, and mobile software migrated from C++ towards managed languages, and they never looked back. I’m pretty sure the majority of embedded software gonna follow the same path.
I also like some of the abstractions. They're clever in their simplicity. For example, an iterator over an array or vector is simply doing a for loop using pointers.
Rust goes all out on eliminating undefined behavior, and may well be the best way of doing that in low-level programming, but eliminating UB is just a means to an end, UB is not the only source of dangerous bugs, and if the cost of eliminating it is high it may not be the best way overall of reducing bugs in low-level languages.
It's worth pointing out that Rust combats more than undefined behavior. Undefined behavior is a category of bad things that Rust works to eliminate, but Ownership goes beyond eliminating UB, and eliminates a large swath of potential logic bugs.
Most people that starts using Rust get stuck on issues that you woudn't have with other languages.
But all of this frustrating learning curve comes to fruition later in when writing code because the experience is closer to pair programming with the compiler rather than trying things in a vacuum on your own.
Make a distinction between programming and the overall programmer experience. C++ is not a simple language and it feels like some of the the rules are arbitrary, vendor dependent, or for the same of backwards compatibility.
When programming C++ at scale you start dealing with manifest files. Different understanding of C++ (templates/precompiler? exceptions? std? the list goes on).
Rust on the other hand is mostly pleasant. Sometimes I have problems with cargo. However like C++ build systems made life legitimately miserable sometimes.
I'm really excited for Rust. I think that at some point it might replace large parts of the OS. I can imagine important parts of the internet (DNS servers, caching servers etc) running on raw hardware. Your OS eats too much of your perf.
You are right, some parts of Rust are tricky (still working out proc_macros) however you get a lot of power from these.
It also feels like the Rust team really thinks ahead.
Like C++ is still working on the Range type. Originally, it was supposed to be in C++03 but that didn't happen and here we are 17 years later still waiting.
You can say things like that the type system doesn't support higher kinded types and such. You are right and I think that at some point, Rust will get them, right now there are more important battles. Whoever manages to implement them will be crowned the king of Rust I'm sure.
What tools are these, and why haven't they been deployed across every C codebase to prevent memory safety bugs?
For example match statements need to be exhaustive. Your program won't compile unless they are.
With Rust the compiler is already providing a lots of analysis, and you can't easily skip it. And if you do by using unsafe{}, it shows. So basically, even if your dependencies are provided by the worst team in the world, you know that the guarantees enforced by the Rust compiler are in the code. (To be fair, it does not mean that the SW will do anything useful, but it won't likely segfault)
I believe the language server is just for real time editor integration.
Converting it from what the compiler knows to the https://langserver.org/ protocol.
The long-term goal is to have the compiler be able to do this directly, but there's some stuff to do in the meantime.
This is basically what all type checking does. It's not specific to Rust.
The borrow checker is unique to Rust. So is compiler enforcement of only allowing thread safe data structures to cross thread boundaries.
Both of those are a pretty big deal.
> % of developers who are developing with the language or technology and have expressed interest in continuing to develop with it
Because there aren't that many Rust jobs, almost no one has to use it. Which means people either try it and leave, they like the language and stick around, or they never use it (the overwhelming majority of developers). This leads to the Rust being the most loved language by the above definition, which is completely different from "popular" (as in the title of the OP).
If I make up my own language that I plan to never stop using, even if no one else ever touches it, I'll get 100% "Most Loved" on StackOverflow's survey.
As you said, mainstream languages won't win this one. But it is a valid metric anyway, and there is plenty of merit on winning it.
But most of the time, people can't stop using languages.
But it's not the answer of everything, to me it's nowadays abused in a lot of projects, there are instances of project where using Rust not only doesn't make a lot of sense but also can be slower than another languages, there are cases where Go is better, other where Node.js is better, other where Python is better, other where Java is better, etc. To me the Rust community seems to want to argue that everything should be rewritten in Rust, just because Rust solves every problem, while it's not.
> Lots of these things that look like syntax problems are in fact design problems surfaced by the compiler.
A lot of people are finding value in the rules Rust provides. This is irrespective of domain. The strict compiler helps you refactor code in ways that less strict languages don't. You still can do it, of course, you just have a lot less help.
The trick is, is the initial hit in productivity worth it, until you get up to speed?
That said, of course everything doesn't need to be in Rust. But there's a reason it's finding broader appeal than purely systems stuff.
That said, Rust’s domain does seem wider than just systems programming. It has the right abstractions to be suitable for (edit: some) higher level things like web apps.
Personally Rust is turning into what I was assuming and hoping Go was going to turn into back in 2010: a static language that does most of what we all used dynamic languages for, without too much extra difficulty. (That is, after you get used to the borrow checker’s rules.)
Frankly any language that makes me write "some string".into(), and all of the other nonsense of Rust, is not suitable for writing string processing applications (as opposed to utilities, such as ripgrep). The business logic of any Rust application that deals mostly with strings is hopelessly obtuse and/or obscured. Languages like Perl, Python and Ruby, or even JavaScript and Java, are more suitable.
Rust focuses on and succeeds at being an improved C++, no reason to use it for everything.
Do you have any hypotheses about why we don't see as much publicity for Haskell then?
I think Haskell is just too abstract for most of us. I love Rust traits and see how it directly came from Haskell but I couldn’t learn Haskell even after like 3 serious tries. Maybe a good portion of the problem is in the documentation and not the language? That said, the extremist purity is simply impractical compared to Rust which gets 90% of the way there without forcing all the architectural dances Haskell does. Just my opinion.
I can't add much to the Haskell part as I've only ever used it sparingly. In many aspects, I view Rust as only having one or two small things that are new (or at least new in a non-research programming language). Its big strength is combining a lot of existing good ideas in a principled manner.
Not too bad for an abstract experimentation.
Add a single other resource, and now you need resource management. Once you have resource management, memory is the easiest thing to plug into it.
If you start out having resource management, you never wonder whether GC would be a good idea. GC turns into a solution desperately looking for a problem, and finding none in your neighborhood.
That explains why it was so hard for me to learn: I'm awful at math. The most advanced math I could understand was high school algebra.
There are plenty of such people, and they need something to do that keeps them out of the way of people interested in solving real problems efficiently.
I think I see more posts about Haskell than Rust here on HN. (What makes sense, since fits a larger set of applications.)
Of course, Rust gives you a lot in exchange for the reduced velocity--safety and performance; however, those are tradeoffs and we should talk about them as such. It feels dishonest to present Rust as "as productive as Python" even with the "(given enough experience)" caveat.
It really depends on how you measure productivity.
But also, I don’t think that “makes you think about memory” is that simple. I don’t have to think about memory in Rust very often at all: that’s what the compiler is for! When I make a mistake, it corrects me, and I’ve only thought about it for a few moments.
> But also, I don’t think that “makes you think about memory” is that simple. I don’t have to think about memory in Rust very often at all: that’s what the compiler is for! When I make a mistake, it corrects me, and I’ve only thought about it for a few moments.
Ok, you don't think about it, but the back-and-forth with the compiler still takes a nonzero amount of time that you don't have to deal with in a GC language. At least not until you start to hit upon GC performance issues, at which point the value proposition shifts in Rust's favor, which ultimately brings me back to my point about it being an issue of tradeoffs and my skepticism about Rust-as-silver-bullet.
But you do pay up front! That's a big deal if your design is not set in stone (which is not a synonym for "prototyping").
(Again, as always, YMMV.)
I haven't seen this. At most, I've seen the push for rewriting important libraries where security is a core issue in Rust. I don't blame them. I'm tired of C/C++ being used everywhere because of performance, leading to the current-day situation of so much fundamentally unsafe code running the world.
As in C or C++. I didn't mean it as its own language. And C++ has plenty of issues where I'd consider it equally unsafe. Unless you're a god tier programmer, and even then, it's impossible to write safe code without insane amounts of effort that can't be expected in most projects.
I don't know about node. But there's whole lot of java code and libraries out there. Otherwise why would people build other languages for the JVM?
Ease of writing; Most of code is okay having a GC. But time to produce working code (not necessarily 100% correct code) is important.
Rust is created for low level stuff. It is wrong to expect web backend programmers to deal with ownership, stack-vs-heap and all.
I am a web backend programmer. I respectfully disagree. I would much rather deal with ownerhip and related issues than deal with dynamic typing fallout. Unfortunately, I am stuck in Python land for some foreseeable future.
> But there's whole lot of java code and libraries out there.
That is true about every other popular language. Rust might not have the ecosystem, being a lot younger, but why would anyone pick Java out of all the other options?
As far as I can tell, the only two reasons people continue coding in Java are "We have an existing Java ecosystem, it is easier to continue doing what we have been doing than to switch" and "Java is what we know best and it is easier to use Java than to learn new technology stack".
Those two things aren't connected. You can avoid both of them by using one of the many statically typed languages with a GC. It might be the case that Rust has other advantages over those languages that make power-assisted manual memory management a cost worth bearing, but it is still a cost. The amount of successful web backends written in Python/Ruby shows that performance demands on a typical web app are nowhere near high enough to justify doing away with the GC.
1. It performs well - we're a small company, so hardware costs matter to us. Its a similar performance class as Go and C# - if you want to go faster, you likely have to use Rust, C, or C++.
2. As said, the ecosystem is huge. If you need to do something, there's probably a library or tool for it. If you have a problem with one of their libs, someone else had the problem too. Unless your business is consulting, spending your time going down paths no one else has gone down just so you can use a shiny new language is a waste of resources.
3. The language isn't full of too many concepts. This makes it easier to get people up to speed with it if they're unfamiliar. I like Rust for some personal projects, but lots of the complexity of the language is completely pointless for most of what we do at my company.
4. The JVM is great.
5. We already know it.
Even ignoring #5, our priority of languages is probably C#/Java, then Go. Java is a meh language. And I feel like the annotation soup only makes it worse: it makes Java both verbose and magical, a truly shitty combination. But the ecosystem is awesome. The performance is good. The JVM is great.
With .NET Core, it might make sense to look at C#. But the language is fairly similar to Java anyways. Switching for switching's sake seems pointless. And Microsoft only relatively recently started officially supporting Linux, so I would be wary of using it for greenfield, much less switching our existing company.
Kotlin is a decent contender here too. But it's not that widespread as a backend language, at least not yet. If I could pick any language, I would choose Clojure because it benefits somewhat from Java's ecosystem and is (IMHO) the best JVM language. Immutability is killer, and I find dynamic typing with specs a good middleground between purely dynamic and purely static languages.
But Clojure makes things pretty difficult when it comes to the hiring end of things and developer familiarity. If I worked at a greenfield company solving the same problems I do today, Java would probably still be my first choice.
Scripting? I'm not gonna write a 50 LOC single-use script in Rust.
JVM tooling is also pretty nice. Can you trivially get deep app metrics from any Rust program?
1. Rust-the-language doesn't know anything about allocations, so strictly speaking, it cannot. Furthermore, without a GC, it's unclear how it could in the first place, given that a compaction would invalidate all pointers to that memory.
2. Certainly not to the same degree as the JVM, given that there's nothing inherently to inspect, like there is in languages with a runtime. You can instrument your own application, but that does mean doing the work yourself, and you only get as good of results as you put in in the first place.
AIUI, Rust knows more about allocations than it should, since it's still limited to a single global allocator, and Box<> (which relies on that global allocator) is implemented via compiler magic and cannot be supplemented by equivalent library code. If Rust supported "pluggable" allocators in some fashion, fancy features (including heap compaction, although I find that a bit dubious TBH) might become easier to implement.
It's coming, but it's still unstable and being actively developed
There are some platforms and ecosystems that pour 90% of the effort into a handful of popular niches, but once you leave that comfort zone you’re on your own. Elixir can feel like that; Ruby/Rails too back in the day.
It just has language features that make sense for the web, which is a huge sector. (parallelism, memory safety, speed, JS interop)
Your overall point is absolutely correct though. We couldn’t have done this without LLVM.
- https://rust-lang.zulipchat.com/#narrow/stream/189540-t-comp...
However, modulo LLVM bugs, the worst to be expected from a frontend selecting a different set or order of LLVM passes would be a loss of codegen quality, which may not be particularly crucial for non-C++-shaped languages.
However not all of them are. Back in the pre-async/await days you'd end up with some really gnarly implicit types in some really big functions with lots of returns. Then sometimes you'd make a mistake on one of the returns and the compiler would assume that your mistaken type was the right one & point out all the places where the actually correct type was returned. Then you'd have to go hunt for where you needed to add a `as Box<Future<Item = _, Error = _>>` to get it to compile again.
I haven't hit any issues like that since I started converting the gnarly stuff to infinitely simplier async/await code, but I have to imagine those kinds of issues are still lurking in other areas where you can't get away from complicated code.
Even with that I whole-heartedly recommend anyone that's thinking about it just go try it out and/or read some of the book. It's a delightful language to work with. I have way more fun working with Rust than I have with any other language. It takes care of so much of the BS that I don't want to have to think about, so I can just think about the interesting stuff.
And will do, if I encounter them.
Years past we spent a lot of time honing general common errors to a pretty neat level, then we moved on to detecting specific cases. I would say that the quality of the output is a bimodal distribution with a cluster of really high quality for common errors, and a big but shrinking cluster of confusing errors for more advanced features.
To give an example, async await relies on three things: syntactic sugar (which means there's generated code that's not in the user written code), associated types (that had badly confusing type errors) and impl Trait (that had confusing type errors). The later two features didn't receive much love for a while because they were advanced features that a newcomer would be unlikely to hit, with exceptions for crates doing interesting but confusing things with them to provide a nicer API. But when the dust started to settle aroune async/await it was clear to me that people would be seeing the stabilization announcement, consider trying rust and jump straight to very arcane errors that would now be very easy to hit. This made me and a few others actually spend the time to drastically (if not entirely) improve those errors ahead of async/await stabilization, with the nice side effect that those features are also up to the level of quality that we want for newcomers. The syntactic sugar part also plays a part on how you explain succinctly and clearly that the type or inference error you're seeing comes from a return type materialized out if the aether, but I think we've gotten good at that.
If you will be picking up Rust to try it out, I encourage people to use nightly, not because of features but rather because we have diagnostics improvements daily and they can be the difference between having to read the book to understand how to fix your issue and just following the compiler's lead. As an example, you can see this recently landed PR: https://github.com/rust-lang/rust/pull/68267
I've recently been using Flutter and it's a delight to get an error message like "you did X, but Y was uninitialized, so you need to put a call to Y.Z in your code first". When my problem is some conceptual mistake, a traditional error message, like "null pointer error in Y" just confuses me, because I don't even know what Y relates to what I'm doing.
I love that you and others are taking the time to see what really went wrong. Partly because it solves my immediate problem, but also because I know that's exerting backpressure on confusing language and library features, which I hope will drive more platform improvements down the road.
It's still possible to hit some edge cases where error messages aren't as helpful as they could be. But these are considered bugs and so will hopefully be fixed.
Some errors are admittedly worse, and people like estebank are actively working on improving them.
I am in that position at the moment. We are a multi-language shop, and there's some components that make sense to write in Rust.
But actually a lot of the big tech companies are using rust, they’re simply not advertising jobs.
- machinist
- maintenance worker
- crater/packer
- painter
I wish there was a better name, still better than go, although they successfully adopted golang so now it is easier.
At this point, this is not true. There are significant production deployments of Rust. Heck, Rust code helped me post this comment, and read yours! (Okay that's strictly not true because I have Chrome on this computer, but had I posted from my phone, or from my other computer, it would have been Firefox.)
A lot of why this is hard in C/C++ is memory management. Even if you had a package manager it would be hard to compose libraries together unless they were designed together (like boost for C++).
The Standard C++ Library supports user-managed allocators for all the containers, heavily used in embedded, low-latency, and real-time applications to get deterministic performance.
Lack of standard support for user-provided allocators indicates immaturity. E.g., C++ lacked them until 2011.
There are subredits of every kind, even great ones. But they change with time.
Another point is the tight integration with cargo, which may lead to the same dependency problems that npm has. But it will cause significantly more struggles because of licensing issues. While most npm code was running on servers, it didn't matter. In the embedded space companies typically insist to have every library checked by a lawyer before it gets shipped with the product, which will make cargo essentially useless.
The last one is the community. In fact I think a programming language is to the largest part about the people using it. And Mozilla sadly created a hype and a hostile community by aggressively evangelizing the language.
That's not a bad description of Cyclone, Rust's direct predecessor. It went nowhere because it was so "conservative" over C as to be deeply unintuitive, and it lacked features that matter to modern programming. Rust is nonetheless far less complex than C++, and that in itself makes it a plausible C replacement.
Before Rust, everyone simply assumed that it was impossible to create such a programming language since it would have otherwise been what everyone used, and now that it turns out that it's possible, it's clear that it's the best solution and what should be used in the future whenever possible.
https://stevedonovan.github.io/rustifications/2018/08/18/rus...
Control over low-level details is something that exists and should be in any system-programming language. How is that even a benefit? It is a niche feature.
Memory safety? You can circumvent it by using "unsafe" and as recent Actix case shows it can propagate really deep. Should I read the code of any other Rust library now? What if I am a programmer newbie driven by the hype without a clue what does memory safety entail? What-ifs... I won't talk about compile times and the fact that in order to compile some cli tool one has to download ~200mb of rust "stuff". This is simply insane and I refuse to understand how is this considered to be not an issue.
Rust ecosystem is probably its strongest point right now which is somewhat funny because it grew out of beliefs in the above "benefits". So, not hyped, sorry.
Isn't the point the combination of features is unique?
It seems like if I invented a functional sledge hammer that only weighed 1oz, you'd say:
> It can hammer big nails? So can other sledge hammers!
> It only weighs 1oz? So does a plastic toy hammer!
> So I don't care.
This is actually an explicit design decision; it means that the analysis is tractable, and that you don't get spooky-at-a-distance error messages.
The number of times I see people recommend "just cut and paste in whatever type the compiler said it expected ..." just makes my hair curl.
It also tends to make the code more obscure and unreadable, sometimes completely so.
Promoting Cargo as a way to build C++ and C programs could be a way to establish Rust underpinnings. Cargo is better than most other build systems now used for C++, and with C++20 getting modules, the fit is improving.
The Oxidation page [1] seems to give an overview where it is.
If Rust manages to actually supplant C or C++ it would very likely require decades and generational change.
https://insights.stackoverflow.com/survey/2019?__hstc=188987...
There are some backend web development deployments at other companies, but the ecosystem has been changing a lot over the last year. It’s starting to settle down though. Expect more “Flask” and less “Rails.”
I just made the point, because from security point of view, we all have to gain no matter what safe systems programming languages get adopted.
It doesn't need to be Rust vs the others, in what concerns improving the current state of IT security.
For example, Nvidia is now adopting Ada/SPARK for security critical firmware.
#include <string>
#include <iostream>
#include <string_view>
int main()
{
std::string name { "Vivian" };
auto nickname = std::string_view { name }.substr(0, 3);
name.clear();
std::cout << "Hello there, " << nickname << "!\n";
}
which will happily compile, but whose behavior is undefined.`std::string_view` references that copy. `std::string::clear` destroys it, leaving the `std::string_view` dangling.
1. Print the nickname before clearing the name so that nickname is still valid when you print it (swap lines 4 and 5).
2. Make nickname a copy of the first three bytes of name, rather then a reference to the first three bytes of name.
let nickname = String::from(&name[..3]);In C such code would compile, what's worse, it would work and behave correctly most of the time. If your code was more complex (for example using threads) you would get a bug that the code would work fine most of the time, but once in a while it would display garbage.
The `nickname` variable holds onto a reference to the substring of `name`. Subsequently `name` is modified (cleared).
This forces you to think about the behaviour. Should the nickname remain constant even if the backing `name` string changes? Probably yes, so you'd take a copy at that point.
> One of the biggest benefits of using a systems programming language is the ability to have control over low-level details.
I think this is a misguided conflation of "systems programming" and "low-level programming", those are separate things. See http://willcrichton.net/notes/systems-programming/
C++'s and Rust's ranges stretch from C's at the bottom up to near ML's. Java is stuck in a little space in the middle.
Rust is not doing that, and that's a hurdle.
In my view, the "C flavor" is very important because it's a standard of how people are able to read code. This has nothing to do with the C programming language, I'm just saying that simplicity is what made C popular and easy to use. I can understand that Rust brings very nice innovations, but in my view it should have done so while keeping the "C flavor" as most as it could have.
I admit it's a conservative view, but I tend to believe that a language can be more easily adopted if at its most basic level, it's simple to read for beginners. Rust is not great: I'm not talking about its learning curve, I'm just talking about the "flavor". A language is a tool of expression and communication, features matter less than readability.
if (foo) bar();
is if foo {bar();}
Same length, but without possibility of "goto fail" error.Rust may look ugly, especially with generics, but on the technical level there isn't much wrong with the syntax. It's quite readable once you know it. And you can grep function definitions!
fn add2(x: i32, y: i32) -> i32 {
x + y
}
variable declaration is varname: type, it makes it very hard to read when declaring arrays or other thingsreturn type is after the paren
the let keyword seems redundant/unnecessary, a little like var in js
Those are good example of "c flavor". I guess they're easier to parse, but still I would rather have C flavor instead.
I personally find the "varname: type" syntax easier to read.
Also, keep in mind that Rust has type inference, so you can say things like "let foo = 37;", and the compiler will infer the type of "foo". The "varname: type" syntax is more compatible with optionally specified types, because it nicely fits in if you want to add the type info explicitly: "let foo: u64 = 37;".