Rust 2019 and beyond: limits to some growth
graydon2.dreamwidth.org
graydon2.dreamwidth.org
I love the intentionally limited scope and the amount of thought that went into writing this, and I wished I had the clarity required to be able to do this. Especially the bit about 'negative space' got my intention, that's one tool I'm going to put in my toolbox to re-use. And there already is one example from a company whose founder I knew that made serious bank on just knowing what they were not, so this advice has applicability far outside of computer programming language design.
Thanks for posting this.
One of the most profoundly useful things I've ever realized in my life is that the most important choices are often about deciding what not to do. Creativity is deeply wedded to the idea of constraints and pragmatically, there are only so many hours in the day. Deliberately deciding to not investing in one thing is the clearest way I know to free up mental capacity for the things I do want to put care into.
Both ways are perfectly possible, and both would in many cases be much preferable to having an implicit "is it time yet for X?" on the agenda every time the version number is incremented. "Not in the foreseeable future" is effectively understood as a far out roadmap item, no matter how much you don't mean it that way.
I'd rather see a humble "we thought that we would never X, but we were wrong" repeated for many different X than a single misleading "we absolutely commit to not be adding GOTO before 2025".
That relies on having a decision-making process and culture that accommodates that. In my current job I'm stuck dealing with inadequate tools because of tool choices made in 2002 that no-one has the political capital to revisit.
In practice (and with some experience) most decisions do not prevent revisiting them later.
People claim Rust is a better C++ and as from my outsider point of view it looks like it is. One of my fears is that it will follow C++ down the very road the author is concerned about.
Simple, only for those that never read ISO C, or tried to write actual portable C code, instead of using gcc C and GNU/Linux everywhere.
For example, {} style initalisers were added to simplify and "unify" things. Except to make a vector of length 3 you still need to use the old style (3) notation. So now there is just one more thing to learn.
It is much easier to add "one more thing" to a language than it is to later take it out.
And C++ is usually way uglier and far more complicated. I'm currently reviewing a bunch of code using bleeding edge language features and it is extremely difficult to understand what the code is supposed to do and what it might be doing.
The age of multi-vendored languages like C or C++ for systems programming might be coming to an end. I'd wager this is actually a good thing.
I have been using Rust extensively for over 2 years.
In those two years, the language complexity has increased substantially. It's already at a stage where I'm worried about losing track.
Also, there is quite a large amount of changes in the pipeline that were already accepted but are either not finished or not implemented yet. All of those can/will have a significant impact on idiomatic code and API design and will increase the complexity burden:
* specialization * generic associated types (a simple form of higher kinded types) * async/await + generators * existential types * const generics ...
So I do believe Rust needs to slow down considerably and get much stricter with accepting new features. Both to not overwhelm the existing userbase and to not make the language another C++ in terms of complexity.
And while I think Rust could do just fine without GATs, specialization is something I'd really hope for.
People will never be satisfied though and always ask for new features.
So I don't agree that the age of "The age of multi-vendored languages like C or C++ for systems programming might be coming to an end.", unless you mean we will managed to get rid of all OSes written in them, specially UNIX flavours or GPGPU libraries.
It would be nice, but it won't happen until the next big hardware revolution like Quantic computers being developed in Q# for example.
That's because you can't make money writing compilers anymore.
I thought that sqeezing MIR in the middle Rust could get both Rust-specific transformations and then benefits from the LLVM generic code optimizations...
LLVM is fantastic, but it's also a huge C++ component, and it's not necessarily optimized for speed. It would be nice to end up with something in Rust to simplify the toolchain needed, and to maybe have something that could be even faster for debug builds.
It's sort of another worse-is-better dichotomy.
As you gain experience in Go, you learn idiomatic ways to do things and memorize the core library, but the fundamental mechanics don't change.
As you gain experience in Python, you develop a preference for virtualenvwrapper and experiment with 300 different ways to build a distributable package, then start extending __getitem__ and __setitem__ and adding decorators to everything and before you realize what you've done, you've torn apart the laws of physics and your code has nightmarish side effects lurking around every corner.
Alternatively, you glimpse the chthonic horrors lurking at the end of that path, purge your code of hidden mutable state, and use Python to write straightforward procedural programs.
The move from C++ -> Java -> Go is a perfect example of this cycle.
C++ is a language designed to enable more communication between the compiler and the programmer.
Go is a language designed to enable more efficient communication between programmers.
C was hardly efficient in the 80s micro-computers, and outside Bell Labs people were doing compiler optimization research in languages like PL.8 and similar.
Had Bell Labs been allowed to sell UNIX and history would certainly looked much different.
For instance, Fortran is very performant in numerical computing, even outperforming C, but it has difficulty in accessing I/O mapped registers or implementing an interrupt handler, a jump table, or just cleaning a particular chunk of memory addresses.
"Oh, it was quite a while ago. I kind of stopped when C came out. That was a big blow. We were making so much good progress on optimizations and transformations. We were getting rid of just one nice problem after another. When C came out, at one of the SIGPLAN compiler conferences, there was a debate between Steve Johnson from Bell Labs, who was supporting C, and one of our people, Bill Harrison, who was working on a project that I had at that time supporting automatic optimization...The nubbin of the debate was Steve's defense of not having to build optimizers anymore because the programmer would take care of it. That it was really a programmer's issue.... Seibel: Do you think C is a reasonable language if they had restricted its use to operating-system kernels? Allen: Oh, yeah. That would have been fine. And, in fact, you need to have something like that, something where experts can really fine-tune without big bottlenecks because those are key problems to solve. By 1960, we had a long list of amazing languages: Lisp, APL, Fortran, COBOL, Algol 60. These are higher-level than C. We have seriously regressed, since C developed. C has destroyed our ability to advance the state of the art in automatic optimization, automatic parallelization, automatic mapping of a high-level language to the machine. This is one of the reasons compilers are ... basically not taught much anymore in the colleges and universities."
-- Fran Allen interview, Excerpted from: Peter Seibel. Coders at Work: Reflections on the Craft of Programming
The reason why older languages are complex and inelegant is because our understanding of how to create simple yet useful languages has increased, not because older languages accumulate much complexity.
One of the big differences is that Java and Go have straightforward context-free grammars, mostly, and you can whip up a working parser in no time. C++ is a bit of a beast, by comparison (hence keywords like “typename”).
C++ also has the complex overloads and template system. I think people underestimate how complex these things are when they are learning C++, and how complex their interactions are. Then there’s the preprocessor.
You can kind of argue that these are just an accumulation of changes, but other languages contemporary with C and C++ do not suffer from these complexities, so the argument falls flat. By comparison, Go and Java rely on reflection or code generation more, and these are a bit simpler.
And then there is arcane stuff like this:
* https://en.cppreference.com/w/cpp/language/eval_order
* https://en.cppreference.com/w/cpp/language/copy_elision
That does make the language feel very complex even without the whole templates depth.
https://www.youtube.com/watch?v=_ahvzDzKdB0
or as PDF:
https://www.cs.virginia.edu/~evans/cs655/readings/steele.pdf
It is like designing a workshop. You only have so much space within arms reach. Here you place your most frequently used and valuable tools. You can "grow your language" by adding tools but they can't replace what you have in this limited and privileged position - instead, new tools have to go in cupboards or on another table. The new tools will have higher friction than the first priority tools you added.
Maybe you can have a workshop where you imagine a tool and it appears in your hand thereby removing the constraint of "tools within reach" but I think this then makes it too ephemeral and abstract. It is the constraints that makes a language tactile and ergonomic - remove the constraints and you have no structure at all.
In this case, the "tools within reach" are your core keywords and syntax. Growing features via libraries or syntactic extensions generally incur more ceremony and less elegance. Having totally flexible syntax extensions/keywords doesn't solve the problem, it just moves up a level abstraction/generality and means you have given your users the "design a language" problem instead of solving it for them.
This is an interesting observation that reminds me of
> In Lisp, you don't just write your program down toward the language, you also build the language up toward your program.
from Paul Graham's "Programming Bottom-Up". Some people would consider it an advantage being allowed to fold the "design a language problem" into the overall problem.
More, I think this is just a matter of degree, not kind.
As soon as you have something as apparently simple as named procedures, you're really writing a DSL, albeit very coarsely, for your business problem. Add named record types. Add textual macros. Add operator overloading. Add Lisp-style macros. At every point where the language allows a word on the screen to stand in for something larger, you're giving the programmer the power to design a language for their problem domain.
> We need to put tools for language growth in the hands of the users.
Guy's previos work with Common Lisp is epitome of this. There are three kind of macros in the language: reader macros, macros and compiler macros. They give the tools for the user to extend the language. There is just 25 or so core primitives in the 'core' language and the runtime. The rest 900+ functions and symbols are basically the standard library (the fact that they are slapped into the same package and they extend the core in a ways that other languages can't hides the simplicity of the language somewhat).
The creator has repeated this philosophy a few times:
> There is very little reason for an Elixir v2.0 with breaking changes. The language was designed to be extensible and if we need to do major changes to the language to improve the language itself, then we failed at the foundation.
https://elixirforum.com/t/what-would-you-like-to-see-in-elix...
> A big language does not only fragment the community and makes it harder to learn but it is also harder to maintain. It is also why the language was designed to be extensible: so the community could build what is necessary without a push to make everything part of the language.
https://elixirforum.com/t/questions-about-property-testing-s...
> We also understand there is a limited amount of features we can add to the language before making the journey too long or the language too big. Adding something now means not including something else later. As an exercise, let’s see a counter example of when we didn’t add something to the language: GenStage. [...]
https://elixirforum.com/t/questions-about-property-testing-s...
C++ were very performant, and unlike Java not a memory hog, but for this sort of tasks it was deemed to complex. Besides the ammount of people that were able to program in it was limited (and you can even see this reasoning being used as a reason why they created Go).
Then Go showed up with simplicity and a good story in performance and memory, deploying simple binaries in a world of complex server infrastructure paved out by Java was a very handy and right on time approach.
The problem with Rust now, is that it is target the system programming, in a era of system programming renaissance given the whole mobile app scenery.
The problem is; there is already a lot of C++, C, Java, Objective C and Swift code there, so it will be hard to have a good reason to rewrite complex stuff with a lot of man hours in it in Rust.
Every language needs a platform. C is here because of Unix, C++ because of Unix, games and early 2000 and 90's era startups. Java and C# because of bussiness software, Go because of the cloud, Ruby and PHP; because of webservers, Rails an blogs, Python for educational purposes, Data Science, ML and now AI.
Now what about Rust? Its trying to eat some lunch from C and C++. But a lot of code and value is already there in C and C++, where rewriting it in Rust just for some ocasional added value here and there is not reasonable.
Its really tough, and if Rust dont find a platform to grow it will be very hard to advance any further. But, there's always new tech waves and tools that will marry perfectly with them.
Lets see which languages are able to be a perfect fit for them in years to come. But right now Rust will depend a lot of the community it already formed to keep sharp and maybe be lucky to surf one of those waves.
For existing software it certainly is less about rewriting it but extending it in Rust.
Thanks to not having a runtime and being quite easy to create a shared library with a C-ABI, pretty much any software written in any language could be extended with code written in Rust.
Ok, but you kind of added complexity to your codebase. Now you will need not only C++ coders, but also Rust coders. If you are Mozilla or Google you can do that, you eat complexity for breakfast.. But they are probably not the companies you need right now to keep your steady growth, unless of course you are lucky that they created some killer app in Rust because of their in-house use of it. Like Google did recently with Kubernetes, only it is in Go of course, but i guess it checks the square of a killer app which helps into the language adoption as a programming trend.
Speaking of Go, it once was suffering from the same problem Rust is going through right now. It was sucessful in a first phase because of its community, but it needed more to start having more adoption and mind share. Then, Docker happened, and Go found a sweet spot to aim for and take it to the second base.
Another platform is cryptocurrencies/blockchains- Rust lends itself uniquely as a modern C++ for writing performant blockchain protocols. This is evidenced by the Parity team using Rust exclusively for all their projects. As that world evolves Rust can find a foothold there as well - there is no real legacy code floating around, the Bitcoin protocol is only 10 years old.
The blockchain trend reminds me that Rust could also try to find some cosy home in banking software, in expert systems and in embedded software.
The key now is to be humble and not to try to pick up a fight with C and C++ just yet.
Go only now is in a position to pick a fight against Java in the server and in the cloud, and Rust will probably need to get into the same point of adoption, if it wants to be seen as a serious contender to those languages in the eyes of the people they need to convince.
Unfortunatelly Rust missed a good new trend now, with multi-platform cores for mobile phones that ended up being written mostly in C++.
Rust wasnt even invented when people were trying to solve the problem of multi-os phone apps.
And that is the current trend which is giving the system development trend a second chance. But is not that bad, because thats exactly what is making people to take a serious look at Rust.
Battery life, memory, storage. It all matters again thanks to the phone environments, and you cant afford the be wrapped in layers upon layers of software virtualization like it was common in the early PC era, where Java and C# were kings.
Why do you think that Rust will be a different story this time?
Nothing -- not even C++ -- "killed off" C.
But it's share of total usage can and will decrease. Java certainly took a massive chunk of the "programming market" that would have otherwise been C++. And then later JavaScript. Of course, software usage in general has been going up and up, so in absolute terms C++ has been increasing as well.
It certainly looks much different in other platforms and modern OSes.
C is very conservative in adding new features, which makes it easy to get an understanding of the language - nothing is hidden, when necessary you can derive everything you need to know about a piece of code from first principles.
C compared to C++ feels like high school math compared to university math with all it's scary notations.
Certainly not in the C compilers that have been ported to C++, the OSes that now push for C++ instead of C, automative standards that replaced C by C++14 as certified language, IoT platforms based on C++,...
The complexity difference between a computer 46 years ago, almost half a century, when C was first developed and now is incomprehensible. It is reflected in how these languages, fundamentally, are willing to just say "since the computer only runs in 30 cycles a second and we are just doing increment operations if you break the rules it undefined" and the idea of having behavior native to the grammar that would impose kilobytes of size complexity or megacycles of computation would have been absurd.
At the time, of course. Modern C++ doesn't hesitate to generate multiple vtables for a complex class while juggling reference counting smart pointers with streamlined initialization grammars that can take up thousands of words of memory. But because the primitives that must remain stable for backwards compatiblity were built on the most constrained architectures the whole house of cards is thus constrained.
I think Rust has already made a lot of "just get it out there, don't think about the ramifications" decisions in its language design, especially around its grammar. On one hand there is still logic to what glyphs do what and how control flow is laid out - particularly concerning minimizing the number of retraced parses the source needs to go through - but it doesn't change the syntax of lifetimes ('), the use of double colon namespacing (::), or the turbofish but being an unintitive disgusting hack.
In my experience the RFC process though has helped temper that. All those blemishes are predominantly from the origin of the language up through ~2014. Once the ecosystem developed and decisions required months or years of hanging around on the issue tracker to see stabilization Rust mostly stopped grafting on arcane behavior and started reusing its syntax in intuitive ways (like impl trait).
I don't think any of the current crop of outstanding wants really makes the language more complex. A lot of it is just increasing the compliers complexity to handle more generic code, be it in terms of kindedness or by type delegation or inference. Those kinds of complexities aren't generally something for users to explicitly learn in rote memorization to use the language - if Rust introduced overloading and default arguments all those who don't know it exists won't have the language made any harder to use, but those that want it can take advantage of it.
As long as Rust is never tempted into the "a substantial portion of this behavior is undefined if not done explicitly as intended" hole that C and C++ were predestined to fall upon I don't think features make the language harder. The core syntax is there, shouldn't change, and is being made easier and more intuitive, not harder and more complex (NLLs save the day, and the number of explicit lifetimes needed have been culled dramatically in the last two years of development). In just a few glyphs of C++: void* = 0 - there are already all manner of edge case, undefined behavior, syntactic complexity beyound comprehension.
Nobody working on Rust today wants arcane, impossible to process grammar or undefined behavior complexity. The language already mostly lacks it. Nobody in their right mind will add it. Thats never a feature, its the bug Rust fixes that is inherent to the C lineage in ways nothing but a rewrite can fix. It took a decade of pain for Python to change its string type from bytes to unicode - there is absolutely no way to salvage C or any of its descendants. By design. From its fundamentals. All the complexity on top has just been repeated attempts to temper the fundamental incongruities.
The following program might no longer compile if `f` could be overloaded, additional type annotations would be required.
fn f(n: u8) -> u8 {
n + 5
}
let o = Some(Default::default());
println!("{:?}", o.map(f));
In general I would expect overloading to make type inference less reliable (not so much with the compiler getting it wrong, but needing more annotations), which users of Rust that do not want overloading would be right to object to.I'd argue you could not implement overloading without enough support being in place to let the current single signature inference work as it does, which is also probably a good chunk of why its never been really proposed in earnest. Its a hard problem to approach.
> Those kinds of complexities aren't generally something for users to explicitly learn in rote memorization to use the language - if Rust introduced overloading and default arguments all those who don't know it exists won't have the language made any harder to use, but those that want it can take advantage of it.
This however is only true if you never have to work with someone else's codebase, When you start creating dialects your usual 'style' will feel alien in another project or you may have trouble understanding how something works due to an arcane language feature you've never had to use.
There are a lot of best practices that can be quite hard to follow without language support, and would generate too many false positives to warn about. For instance, use after free. There is no lifetime information available in the type system that the C compiler could use to tell you whether you are doing something that could result in a use-after-free. How do you propose the compiler could catch these, except in the most trivial cases?
I think that's pretty terrible advice for something that affects memory safety, and it invalidates your entire "the compiler will warn you" argument. There's a reason Rust has ownership and lifetime annotations: there are many things that a compiler simply cannot infer.
I wouldn't say it's terrible advice though. If I see that something I do is potentially dangerous and would require special care to get it right, I'd first look if there is another way to do it.
References are always guaranteed to point to something that lives longer than them. What they refer to could either be on the stack, or it can be allocated and deallocated on the heap via smart pointers. Box<T> is similar to C++'s unique_ptr; there is a single owner, and when that owner is dropped, the referenced value is dropped. Arc<T> is similar to C++'s shared_ptr; it's an atomically reference counted pointer, so the value will be deallocated when the last clone is deleted.
These combine in ways that allow you to use most of those "best practice" patterns from C and C++, but with actual language guarantees that you will follow them and the compiler will catch it if you don't. In the internals of data structure implementation, you may need to go beyond what this allows with use of unsafe, but you can limit that just to a few small critical pieces of code that can be more thoroughly audited and tested.
In a large project "try to avoid doing that" doesn't really fly; in a large codebase, with dozens of developers, developed over multiple years, people are going to make mistakes. They will need to do some kind of refactoring, and one of the guarantees will get missed.
If you've done a decent amount of work in a large C or C++ codebase, you might appreciate this description of the process of storing a reference to a slice of a Vec in Rust, without having to manually reason about all of the places in which that slice could be invalidated: https://manishearth.github.io/blog/2015/05/03/where-rust-rea....
This article, written as a way to help introduce Rust 1.0, provides a few more examples, though they are artificial rather than from a real-world project: https://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.h...
I think it's bad advice because you need to know what is potentially dangerous. That alienates newcomers to systems programming (which is why one of Rust's goals is to be able to "hack without fear"). There are also some designs that I'd never try to build without safety checks, because they're notoriously hard to get right. Even knowing the fundamentals of memory safety, without a way of enforcing lifetimes and mutability, writing zero-allocation implementations that share immutably can be daunting.
[0] Cell, RefCell, Mutex, Rc, Arc
What's got people excited is the evolution of these systems which have a rich intersection between rigor and usability (to include ecosystem, tooling, etc.). The ability to statically eliminate classes of error with good (or maybe good enough) ergonomics means these questions of "how frequently does this occur" are not relevant - you can drive such occurrences to zero and doing so should become table stakes. This is a proper engineering mindset.
None of this is to dismiss craftsmanship, which definitely has its place. And before the ability to practically apply rigor, that's all there was. After all, people have been building complex systems for centuries without the benefit of modern mathematical foundations. But none of those are good reasons to eschew rigor when it is within reach and when the systems you develop are big/complex/widely-used enough.
- the number of users
- the amount of mission-critical legacy code that has to compile
- the number of compiler vendors
- age
... comparable to C++’s.
Did you hear they’re finally making it object oriented? The name will be ADD ONE TO COBOL
I think that was supposed to be a joke, mocking Cobol's verboseness (compare "C++", which adds 1 to C and which extends C with OOP).
It's a new language targeted toward people who want stability and performance, what do you expect to see by now?
When? 2018? 1990?
So far that is only true of Redox.
The change isn't as marked as say perl5 and perl6 but it is still huge.
I've no dog in either fight though, I moved to managed languages a long time ago (Object Pascal was the last compiled language I used in anger).
Not sure which managed languages you mean, those that I use also provide the option to compile to native code just like Object Pascal, if I wish to do so.
(And I also elaborate more about specifics in my other sibling comment)
To what extent is Rust being adopted by the Fuchsia team?
The thing is when I moved back from Rust to Go, I had forgotten how productive Go is and I felt something like this:
https://youtu.be/1i_Eqj7wu88?t=50 (20-30 sec video)
As someone who tried to "sell" rust in my org, I see the biggest issue is:
1. Steep learning curve (it is true)
2. When lot of people talk about #1 it gets amplified in the minds of people who make calls on the tech to be used
A great effort has been spent on #1 and I agree the payoff is worth it. But #2 is perception/marketing issue.
PS: I am huge fan of both Rust & Go.
Though then there's also Luna-lang, which I'm hoping even stronger to become long-term a new breakthrough paradigm/tech in programming & scripting...
On a mostly unrelated note, but continuing with off the sofa predictions, I'm curious on a few more developments:
- whether WebAsm will become the new de-facto universal architecture — at least for VMs, but I wonder if we won't eventually end up living on purely WebAsm CPUs at some point in future? (Unless WebAsm has some inherently hardware-hostile design decisions; I'm not a CPU/ISA guy to know that.)
- then RISC-V; will it become the ultimate personal computer/device CPU in the meanwhile? Also, what may happen if both RISC-V and WebAsm start naturally competing in this domain eventually?
- Fuchsia off-handedly dethroning the Linux kernel & ecosystem (and maybe even Windows) as the sudden standard mainstream OS (and thus also, amusingly, suddenly swinging the Tannenbaum's argument up through a semi-random caprice of Google's deep pockets)?
Especially per the last point, I wonder if we'll eventually end up in a world with much more... "secure"... personal devices. And if yes, will it end up being net good or bad (or just neutral/hard to say/nuanced, a.k.a. both, as usually) for humanity. Given that I'm recently realizing that "secure" unfortunately seems to be a very close sibling, or even maybe just an other face of, "closed" — think DRM, walled-garden ecosystems, no more rooted Android or XBOX... or even no more control at all over your PC, a.k.a. no general computing for the masses, potentially. Though I personally stay mostly hopeful on this front; in part because I think I believe people are as a sum too chaotic for this to be able to happen completely.
These things are always on a spectrum. Just because a spec exists doesn’t mean that it has holes; Go’s spec isn’t formally proven, for example, so you could make a similar claim: how can people know that it all works without a proof?
The answer is that it’s all a spectrum. Many languages don’t have anything resembling a spec at all!
(We are interested and actively working on such a thing for Rust, but we’re shooting high. It’s gonna take a while.)
I would love to see a feature freeze and focus on writing down a spec and speeding up the compiler.
The only thing not mentioned in the book should be HRTB. Did you run into something else?
pub(crate) doesn't seem to be mentioned in https://doc.rust-lang.org/book/ch07-02-modules-and-use-to-co....
Some things that are covered are a bit hard to find. For example, I didn't know if operator overloading was covered; it turns out, it's mentioned in https://doc.rust-lang.org/book/ch19-03-advanced-traits.html, but it's not in the outline and there's no table of contents on that page, and it's only actually mentioned as an example of how default generic type parameters work. The topic it is covered well by the `std::ops` docs (https://doc.rust-lang.org/std/ops/index.html), but since it's a language feature as well as a library feature, I'd expect it to be covered by the book as well.
Raw string literals are only covered very briefly in the appendix: https://doc.rust-lang.org/book/appendix-02-operators.html
#[path = "path/to/file.rs"] doesn't seem to be mentioned; there are probably a bunch of other obscure attributes like this that aren't mentioned.
repr? I didn't find any mention of #[repr(C)], #[repr(packed)], #[repr(align)] etc. I also didn't find union.
simd? Seems to only really be covered by the 2018 edition guide. Googling "rust simd" turns up references to the old, unstable std::simd.
The first two examples are a couple of things I've recently looked for and couldn't find; the others are ones that I noticed browsing around just now looking for other things that might have been missed.
Now, some of these are documented in the reference, in the nomicon, in the 2018 edition guide, or in the standard library docs. But that's one of the problems; that there's no one, or even two or three, places you can go to find everything you need.
The reference has large warnings at the top about being incomplete, although it's actually not as incomplete as I remember, it might be a good idea to remove the scary warning from it, or to do an audit of it and only put the warning on the sections that are found to be incomplete (like the memory model, which is clearly incomplete as it hasn't actually been formalized yet), but take it off of the sections that actually are up to date.
And Google doesn't always help. It still sometimes gives me first edition book links at the top. When I was trying to look up all of the `#[cfg(target_...)]` forms I could use, I tried to search for "rust cfg target", https://www.google.com/search?q=rust+cfg+target, but that gives me Rust By Example, a first edition link, and a link to a placeholder that refers to both the first edition and the reference. It took a lot of clicking through to actually find the reference page on it.
> it might be a good idea to remove the scary warning from it, or to do an audit of it and only put the warning on the sections that are found to be incomplete
Completeness is hard, as you've seen with the book! This is what we'd like to do, but it's non-trivial.
I agree that completeness is hard, but I feel like it could be a bit easier if there were some consolidation of the material.
At the very least, make sure that everything covered in the 2018 edition guide is also covered in the Book, in at least a similar level of detail. I'd also make sure that everything covered in the Reference is covered in the Book, or the Book provides at least an introduction to the topic and then a reference to the more detailed information in the Reference.
They have very different goals, so this will never happen. The Book's job is to teach you how to program in Rust. The referece's goal is to (eventually) be a full specification. The former must be a sub-set of the latter.
I do agree that everything should be documented, but the book cannot cover every last possible thing.
For example, I don't think that every last detail of #[repr(...)] needs to be covered in the Book; but as the entry point for most people, it's how they discover features, so it should at least provide an introduction, and link to the reference for more information.
Likewise, while I think that the std::ops documentation does a good job of covering all of the details of operator overloading, I think the Book could provide a slightly more discoverable introduction to operator overloading which isn't just being used as an example of another feature, default generic type parameters, and provide some links to std::ops which might help with the Googleability problem.
One thing that I think really needs to be focused on is some SEO for the docs. For some reason, the way the Rust docs have been organized over the years, with old versions of docs and the old book being replaced by the new one, means that a lot of times, when I search for something I find old, out of date docs.
For instance, with operator overloading, if I search for "rust operator overloading", I find Rust by Example, which is OK but has a lot of text in comments which is harder to read, followed by a first edition link which warns about being out of date and then provides a link to the new book but not where in it to look, as well as a link to the 1.30.0 version of the first edition book, then a link to std::ops (which if I were a newcomer, I might not realize was relevant), then a 1.5.0 edition of the book.
The first edition operator overloading chapter seems pretty reasonable, actually, and it's present in the table of contents so its more discoverable, and it contains a link to `std::ops`. But because of the new structure of the second edition book, the first page I see if I try to check the book tells me that it's outdated, and directs me to the new book in which this material is harder to find.
And in the new book, even once I find it, there's not much to direct me to where to learn more about the topic. There's no link to `std::ops` documentation; I have to separately look that up.
Part of the problem was that the first edition book was a bit more like a more accessible reference, while the second edition has a more narrative or tutorial structure. So there's no longer a place to go for an accessible reference to all of the language features. There's the Reference, but that's written for detail and precision and not accessibility, and as discussed, is incomplete. There's Rust by Example, but that's mostly just code examples with commentary in comments. The second edition book covers a lot of the same material as the first edition, but because of the different structure, some of the "reference information" on more advanced features is just buried in a couple of "Advanced X" chapters.
I'm not sure of the best way to address this; I admit that getting this right isn't easy.
One option might be to have the new book be in two halves; one of which was the narrative/tutorial portion; chapters 1 through 18, and 20, perhaps, though they might be able to be slimmed down a bit and some material moved to the second portion. The other half which is more of an "accessible reference" for advanced features; the material in chapter 19, but expanded a bit and with more discoverable per-topic chapters like the "Syntax and Semantics" section of the first book, plus the Nomicon material, and any of the topics which are missing.
Or, alternatively, just expanding on and integrating the "advanced X" material, Nomicon, and missing topics into the overall structure of the book.
Or another option would be to have the Book just be the Book focusing on the main narrative/tutorial introduction to the most common features, and have a Reference in two portions; one longer one which covers every major feature but in an accessible style with examples, followed by a more formal appendix covering each last little detail.
I guess ultimately what I'm saying (sorry for taking so long and so many diversions to say it, but as you point out, it is hard to get this right, and I've been thinking about what I'd be looking for as I write), is that there should be some place that has an "accessible reference" to all major features which is organized somewhat like the "syntax and semantics" section of the first edition book. The second edition book, chapters 1 through 18, is definitely a better organization as a book than the first edition, but that structure doesn't lend itself well to covering every feature, and as you point out, you wouldn't want it to.
But the Reference also isn't a friendly place to point people, both because of the "incomplete" warnings but also because it's written in a more formal style.
And the fragmentation into the Book, Nomicon, Rust by Example, stdlib docs, 2018 Edition Guide, Cookbook, Reference, Cargo Guide, Unstable Book, along with some of the reorgs along the way, can sometimes make it a bit hard to figure out the right place to look. I feel like there should be one document somewhere that has a reasonable accessible introduction, with examples, to every feature, in addition to the more formal and succinct Reference.
Maybe I should put my money where my mouth is and try to compile something like what I mean. It would help to have a little bit of buy in from docs team leadership, though, so that there would be a hope of it getting merged and maintained. Perhaps a docs RFC would be appropriate?
One way to start might be to have an official extended cheat sheet. A cheat sheet needn't explain everything, but it should list every language feature available. The idea is to give you a definitive "inventory" of everything available so you can search for answers elsewhere if you see something you don't understand. It should also be brief enough for the language maintainers to commit to updating it when they enable new features.
It is actually fairly comparable to the Rust Reference (https://doc.rust-lang.org/reference/introduction.html), though the way the Rust Reference is divided into chapters makes it a bit hard to see everything at once or search.
They both do contain some examples, as well as things like the syntax in a BNF like format.
Perhaps what I'm looking for is just to have the Rust Reference made a bit more complete and a little more accessible in format (the division into chapters doesn't really seem to help much), as well as improving the SEO since it doesn't show up in Google searches often.
You can also use the print option to see it all on one page, if you prefer.
Thanks for caring about this <3
that's moving the goalposts. let's see Rust's language spec finalized first, then we can talk about it being proven. without the two you shouldn't throw stones.
Of course, formalisms can still be useful, but as a matter of writing style, maybe it's better to put that sort of thing in an appendix? (As sometimes done for grammars.)
With a formal spec you could also derive an implementation automatically which is a great useable reference. Look up the K framework for one possibility.
One can NOT generate a formal spec from an informal spec (or else that “informal” spec would actually be formal, after all).
So, a formal spec is strictly better than an informal one — it enables all the benefits of an informal spec, via the ability to generate any number of informal specs from it in many human languages, cultures, levels of detail, etc., and of course enables things like compiler reproducibility (which you cannot do at all without a formal spec).
That being said, any spec is probably better than no spec.
As a result, neither is strictly better than the other. They have different audiences and serve different purposes. The audience for pure mathematics is quite small.
[1] https://commandcenter.blogspot.com/2012/06/less-is-exponenti...
http://plv.mpi-sws.org/rustbelt/#publications
In order to do this, there also needs to be an unambiguous formal semantics of Rust, so one might even hope that this will turn into some form of official language specification.
I don't think Rust is stable enough yet for a real specification.
I think Rust is already there. It's a concern, but I'm not sure you can or should try too hard to avoid this.
What you can do is look to make simplifications as you add complexity.
And sometimes innovations can have simplifying effects. For example, the various "effects" libraries for Haskell simplify I/O code compared to not having them.
So whatever Wirth has done was quite of interesting in terms of language research for the language geeks among us, but hardly with any market relevance.
You can check Oberon's history on my website.
I've criticized Rust's growth here before. The big breakthrough in Rust was the borrow checker. Finally, one could have memory safety without garbage collection or reference counting. Huge improvement.
"Unsafe" opened too big a hole. I was looking forward to seeing more work on eliminating almost all need for "unsafe". Backpointer support. A way to talk about partially initialized objects. More static analysis. But that didn't happen. Instead, feature after feature was piled on, like C++. Individually, the features aren't bad, but cumulatively, Rust is now a very big language.
Meanwhile, C++ has been trying to retrofit Rust ownership semantics using templates. There's too much "be very careful" associated with that.
Additionally, useful back pointer support (i.e. for anything more than a toy doubly linked list) is probably a bigger feature than anything else added since Rust 1.0, and probably harder to work with.
Are you talking about something else?
Not any more. Not even close. The amount of stuff you have to deal with immediately is far larger now.
I actually miss the days when people added this kind of stuff to their transpiler of choice (e.g. coffeescript) and just used that - it doesn't need to be in the language, because it can never be removed from the language. Let fads appear and die outside that barrier, not inside.
I was experimenting with server-side JS in 2009, before node.js was released, and I hacked on Brendan Eich's Narcissus interpreter, which gave me a good sense of the language.
Now when I look at example JavaScript code on the web, I invariably find something foreign. I'm not saying it's bad -- just foreign for somebody who uses the language only occasionally.
I don't believe JS should be a language only for "full-time JS programmers". I use 3 or 4 other languages regularly, so it's not nice when one language wants to hog my mental bandwidth (unfortunately C++ is one of those languages, so I know this problem well).
It seems like everybody forgot what Crockford was saying back then? He talked a lot about the failed ES4 project (which ironically Graydon Hoare was a part of (?)). And ES5 was very restrained as a result, and I think mostly successful.
It seemed this idea of restraint went completely out the door soon afterward, and nobody even remembers what was discussed 9 years ago. Crockford seems to have moved on (as well as Ryan Dahl). I haven't followed the JS language changes that closely, so I could be wrong, but I have the impression that it's completely changed.
-----
I don't think I'm alone, as I remember that Bryan Cantrill recently said that "Brendan Eich never wrote a book about JS". In other words, the language was left without a founding philosophy. It's been an accretion of convenient features, some of which may be regretted in a couple years' time.
There was a Strange Loop talk about this too regarding the lack of history/culture in JS.
It seems like the same thing may have happened with Rust, since Graydon isn't an active contributor by his own admission.
I was reserving my thoughts about ES6 until I worked with it for a while, but now that I have, I have to say for those reasons you cited I don't like it as much as ES5.
That's not to say that I think destructuring is all bad - it certainly saves lines of code - but aftet working with it for a few years, I would gladly trade it back for more scannable code, easier syntax for beginners, etc.
I believe that the general consensus is that it is to be avoided for libraries. For application development it is convenient, but not perfect and it will probably be replaced by something better at some point.
> It sometimes feel like the language is moving too fast for me to learn it.
It has been moving fast, but expect that to change over the next year! Most of the "Rust 2019" posts have been advocating slowing down.
There is an RFC for pulling some of the trait improvements into the language. After that, I believe they plan to continue to iterate on the design of the failure crate.
The general recommendation I make and see from others is that `failure` is far from stable. Feel free to use it in applications but avoid it for libraries.
Coinciding with the 2018 edition release I ported one of my libraries to it that was using vanilla std::Error impls for two years and dropped about ~400 LOCs out of 600 lines of error handling. It still does all the exact same stuff, and if you use its generic Error impl it behaves the exact same way as the std trait.
If Failure stops getting updated.. fine? It doesn't need changing if it works right now, on the 0.1.3 release, for you. If Rust's error trait changes it breaks all libraries anyway and would only happen in the next edition. Thats probably three years out!
If you expose failure in your public API and failure makes a breaking change, it is a breaking change for your API because your clients need to be on a compatible version of failure.
The alternative is to just use... standard errors anyway? With the aforementioned sizable boilerplate? There's no downside to failure unless there's a less volatile error replacement you'd use instead.
failure is a victim of its own success. I think most everyone believes it's pretty close to what we want Rust's error handling story to look like. But it's a public dependdncy and everyone started using it. Now it's more difficult to evolve.
Yes, but you can only directly depend on one version of failure. What happens if you directly depend on two libraries with different versions of failure? How do you interop with them? In a lot of cases, you'll be lucky and not need to reference the types in any way and it will "just work". This breaks down if you do need to reference the types.
The other thing about Rust error handling is the amount of boilerplate to convert between errors. Error-chain and failure are two iterations on how to deal with it. Failure seems to be the current best practices.
https://www.google.com/search?q=rust+tour
the 1st result is "A 30-minute Introduction to Rust". Going through it requires 4 navigations from the user to get to a valid page, everything in between is deprecated
We have some constraints that make plain redirects really hard; we cannot run a web server or use a ton of JavaScript, for example. Maybe this situation is painful enough for community consensus to change...
Just curious, why are those constraints there?
I personally would love to tell people "sorry, you have to run a basic web server, even locally" for a few reasons, but it doesn't feel socially viable at the moment, sadly :/
1. A first edition that is marked as outdated [1] 2. A second editions that seems to be not outdated [2] but 3. also the book without any edition marker that differs from the second edition [3] 4. As well as the second edition for various rust versions (eg [4] or [5])
And all of them are only distinguishable via the url. I guess the book without any version or edition marker [3] is the nightly version? But I am not sure. I guess the second-edition does not differ much between the rust versions as the chapters look the same, except for bug fixes? I am not sure.
As a beginner learning rust that is really confusing because I am never sure if I am reading the correct book or if there is some other version with more information.
Eg why differs [6] so much from [7] when both are the "second edition"?
Ok, while writing this comment I now see that [2] are only empty pages linking to [8] and that all that books and documentation are generated for each rust version. But my point still stands: When coming from the google search result page to the rust documentation there is some confusion of what I am looking at.
[1]: https://doc.rust-lang.org/book/first-edition/index.html [2]: https://doc.rust-lang.org/book/second-edition/index.html [3]: https://doc.rust-lang.org/book/ [4]: https://doc.rust-lang.org/1.28.0/book/second-edition/forewor... [5]: https://doc.rust-lang.org/1.29.0/book/second-edition/forewor... [6]: https://doc.rust-lang.org/book/second-edition/appendix-04-ma... [7]: https://doc.rust-lang.org/1.30.0/book/second-edition/appendi... [8]: https://doc.rust-lang.org/1.30.0/book/second-edition/
Here's a short history: I wrote the first edition myself, before Rust 1.0. Then, Carol and I started a re-write. When it was ready to be published, we cut the final draft as the "second edition", and forked off new work in what was called the "2018 edition". That's why they're so similar.
However, for various reasons, this situation isn't ideal. So we've decided to only ship the "edge" version of the book, even if it may be a bit ahead of what's currently in print.
I'd love it if there was something interactive like the golang tour but the book is already a great resource. Thanks for writing it
That’s “Rust by example”, by the way.
GHC Haskell and Rust with have an intermediate language (Core and MIR + Chalk, respectively), which keep the rest of the language in tight check. No other mainstream language has this (Java bytecode doesn't really speak to high level safety properties). This ensures the vast majority of the languages are forgettable sugar, and making consistencies of various sorts tractable.
I would argue that even outside of languages as we conventionally think of them, the lack of separating functionality into core languages/interfaces and sugar is the central failure in letting complexity spiral out of control. And indeed, just about everything has.
No amount of pretending otherwise will change that. No amount of separation will change this. It does not give you any more ability to change the syntax over time, or get it righter.
Your users care only about this syntactic interface. In a good world, they do not care how the rest happens in practice.
Worse than this, the idea that a mid level IR should be the "thing that keeps the syntax in check" seems beyond broken. The lower levels do not drive the higher level in roughly any case. Instead, they exist to serve the higher levels. First you understand what users want at a high level, then you try to understand how to make it fast/well. If you need to change the mid level IR to do so, you do.
There are nothing but tradeoffs in mid level IRs, and those tradeoffs change over time based on the needs of languages, not the other way around.
Driving a language based on what you can accomplish in a mid level IR would be incredibly silly - "Welp, we better use this form of parallelism at a high level because we chose coroutines in the mid level IR" should never occur. Instead, the answer is "we change the mid level IR to best support the form of high level parallelism we want in the language"
[0] https://docs.oracle.com/javase/specs/jls/se6/html/defAssign....
Let me response point-by-point
> It's not a thing your users see. The "syntactic sugar", as you call it, is the interface between you and your users.
Users can and should learn the core language too. It's can allow compressing the information in the brain just as it allows reducing the code in the implementation.
> In a good world, they do not care how the rest happens in practice.
Again, I believe the core language is a meaningful thing to study and learn, not just some shove-under-the-rug implementation detail. You clearly don't, and your conclusion is indeed implied by that presupposition. It's hard to argue either from other principles so let me say it is widely held by those that study programming languages and work with cutting-edge programming languages: My axiom has more buy-in by the relevant in-groups. Maybe out-groups with yours are right and we are bad designers, or maybe those out-groups should accept a non-trivial learning curve vs permanent complexity.
> The lower levels do not drive the higher level in roughly any case.
This is wrong. As someone active with RFC discussion for both Rust and Haskell, this empirically not the case. Ideas are constantly proposed and vetted based on whether they fundamentally extend expressiveness or are just sugar.
> First you understand what users want at a high level
Yes, languages changes should and are be driven by end goals, but end goals != surface syntax!!
> There are nothing but tradeoffs in mid level IRs
What does this even mean? Surface syntax is full of tradeoffs too.
> those tradeoffs change over time based on the needs of languages, not the other way around.
Again, once the MIR/core exists, RFCs heavily reference them so this is false.
> Driving a language based on what you can accomplish in a mid level IR would be incredibly silly - "Welp, we better use this form of parallelism at a high level because we chose coroutines in the mid level IR" should never occur. Instead, the answer is "we change the mid level IR to best support the form of high level parallelism we want in the language".
This leads me to think you don't know much about IRs in these langauges in practice. Neither Rust's or Haskell's IR has any notion of concurrency precisely because nothing good enough has presented itself. Both language use syntactic / "encapsulation tricks" (See Haskell using the IO monad to avoid the value restriction of OCaml, Rust's Send and Sync) to chew off some safe subset that works with many modules. An IR with a deep/"semantic" understanding of concurrency doesn't exist for these "production" languages, though I hope http://plv.mpi-sws.org/rustbelt/ could get Rust there someday.
-----
Extending expressiveness vs sugar is fundamentally a property of the high level language, not the mid-level IR. It happens that talking about a mid-level IR is sometimes a nice way to "prove" that something is new or sugar.
As the parent said, the programmer using the compiler only truly cares about the high-level language when writing code. They may dip into the mid-level for optimising their code etc., but they won't write it directly: to a non-compiler author, IRs are implementation details of the programming languages that happen give insight into how some detail of the language works or how a particular piece of code is optimised.
To hammer home this point: if there's some major change required/desired in the high-level language, the current details of the mid-level language shouldn't affect the overall design. The mid-level IR can change to accommodate the desired surface behaviour.
- Old program maintainers (more reading)
- Language implementers
You leave out the 3rd and conflate the second. Others have conceive of PLs as some sort of bargain between human and machine, or human and pure math. Either way, there's multiple interests at stake.
Or, even simpler, what if Rust becomes way harder to learn, but mastery gives one correspondingly way more productivity? Is that a fair trade in your eyes? (I do not believe in practice it will become way harder to learn, but I accept the situation I describe as fair and not a failure of Rust).
-----
You may not write it directly, but you will still think about it. Again the IR of these good languages is like some platonic ideal that sacrifices concision of programs for concision of the language. It is not just an inplementation detail.
Hell, if you insist on thinking that is just some crufty implementation detail, sure. But then let me show you how it's an abstraction that leaks badly, hahaha.
- "Non lexical lifetimes" are probably the most anticipated Rust change lately (see rest of thread). But crucially, they are lexical at the level of MIR. The easiest way to precisely define them is desugar to an explicit control flow language. Otherwise you are forced to look at the a combinatorial explosion of surface-level constructs, or just handwave based in the intuition of control flow.
- https://github.com/thepowersgang/mrustc/tree/master/src/mir this is the alternative Rust implementation's MIR. It's pretty close to the same thing! (Not so with LLVM IR and GCC's RTL.) If mrustc gets to implementing NLLs, it will probably get even closer.
- mrustc does not implement old borrow checking probably because it's sort of an arbitrary spec against the surface syntax. Surface syntax is much more boilerplate to go through all the cases, and while old lifetimes may have a simple enough rough intuition, they are very arbitrary when considered precisely.
And yes, the core language can change underneath the hood, but this is highly unlikely in practice. GHC's core has certainly changed, but this is evolution more than revolution.
I think the heart of what I'm saying is two things:
1. Formal reasoning is the only way to constrain complexity. Humans alone inevitably don't fully graps the entire thing (it's just too big!), and even when they do don't have identical mental models. The results is always contradictory design if any change at all is permitted.
2. Human PLs have lots of conveniences and other fluff (yes, even Scheme, Nix, or your other favorite minimalist language), so the only efficient way to reason about them formally is by desugaring to a core language.
---
I think you're conflating things too: NLL is defined in terms of a CFG, which happens to be MIR at the moment, but if the current MIR is insufficient for some task, it can still be fundamentally changed, with the NLL semantics handled specially, e.g. with a multi-step lowering, like HIR in Rustc.
The mid-level IRs chosen for a compiler are in service of the high-level language and its tools (like formal/static analysis), not the other way around.
In fact, a static analysis or formal reasoning tool likely needs more information than fits into a compiler/optimisation-focused IR. (You see this even with LLVM IR vs MIR and SIL etc: LLVM IR can't hold enough info for the front-end to do everything.)
Seen in this light, I've never such a thing attempted at this scale (GHC's size and GHC/Haskell's age), and very much commend it. Hope other things can do the same thing someday.
It's easy for a language designer to say things are just "forgettable sugar" whereas in the eyes of the users, they are anything but forgettable.
But even discounting that, I think it's still meaningful. Sugar is easier to deprecate; The desugaring can be used to remove deprecated sugar in the wild too. Core language complexity is a lot harder to eradicate.
If we had an assembly+proofs language and a compiler from Rust to it, then I would agree. See CakeML for an example of research towards correctness-proof-preserving compilation.
This doesn't obviously match with Felleisen's notion of expressitivity (which might be used to draw the line between desugaring and general compilation), but I think I like it better. I value invariants over expressiveness.
The absence of HKT / GATs / overloading / default arguments from the language doesn't make it easier for a newbie to learn. These aren't topics someone new, or even intermediate, should ever be touching until they need them. And in the absence of having this functionality (example - we JUST got const fn this month in stable) you have to work around the weaknesses of the language in often obtuse and cumbersome ways... such as hacking the macro system with lazy_static.
I think a lot of this sentiment comes back to what makes a language hard. I recently started learning CSS properly for once, now that Grid is in and I can just do all my styling by hand again in the same way I can use ES6 instead of JS frameworks. I needed Bootstrap and JQuery back in the day because despite the simplicity of the fundamental languages there were things I wanted to do that I could not do in an intuitive way. So other people wrote thousands of lines of code to make what should have been simple and accessible but the languages weren't expressive enough.
On the inverse, you need some degree of intuitiveness to your design. If I didn't read about the syntax of [<name>] col [<name>] col in CSS Grid I would have never intuitively thought you could name Grid regions. The syntax is completely arcane and arbitrary, isn't based on anything else common to CSS, and just happens to be. That is the source of real complexity in a programming language, not how long the book is or how many pages long the standard library is. Nothing worth learning or doing is so simple as to be digested in a weekend and trying to make a programming language like that, especially a systems one - one that has to expose all the jank complexities of how computers actually work - would have you make a really pretty paper weight nobody could use to get real work done.
The complexity in C++ is not in how big the template library is, or how many glyphs the syntax uses, but in how much mental stress it is to see void* = 0 or new Foo and have to juggle all the edge cases and eccentricities of the grammar and model on every single line. Its a massive weight on ones shoulders and that is what will drive a newbie away, at least a newbie with the mindset to take advantage of what they learn. Not the length of your book, so long as all those chapters are about things people want, need, and that are beneficial to the user, rather than warning them about all the ways they can break everything with a single character or how arbitrary decisions were made incongruent with the rest of the language because thats just the way it is.
C++ and C are not complex for their expressiveness, they are complex for their arbitrariness and lack of consistency. Same way Javascript and CSS are complex. Same way an actual, legitimate newbie to Rust is going to have much more difficulty processing why :: is the namespace delimiter or why you need curly braces to deliniate scope than how having the option to declare your function const would. Until they need const functions they don't need to touch them and can write all the non-const Rust they want. But now they have const when they need it and can often never touch something crazy like lazy_static again.
I disagree. :: and curly braces are just a shallow bit of syntax: you see it, learn it and it doesn't interact with anything. It is the easy part of learning the language. For example Go has curly braces too, and it is one of the fastest languages to get started with.
Maybe const functions are not visible immediately, but then as a beginner you would start looking at some documentation, and you'll see const there in API documentation and not understand it, so that makes the language look like it has these opaque features you do not understand. I'm not saying const functions are not worth the cost, I'm just saying that they do have a cost and add complexity in a way :: and curly braces do not.
However, I disagree that simple language feature count has no cost: Assuming “linear cognitive complexity”, even such linear features do have a cost: Sure, you may not need to learn what a “const fn” is until you need it if you’re working only on your own project, but if you’re working on a team and/or with external libraries, the chances that you can get by without knowing the ENTIRE LANGUAGE diminish rather quickly with project size.
This is why even the least costly kinds of language complexity can still end up costing a lot in a large project containing a lot of code, dependencies, and developers.
Once, long ago, there was a new programming language. It was lean and mean. Partly to keep codebases understandable to all, and to make the language easy to learn and straightforward, it eschewed both having too many features, and having any feature that was too abstruse. People marvelled that the whole language, standard library and all, could fit in a nutshell. The name of the language was Java. The end.
The Planning System: hhttps://graydon2.dreamwidth.org/262512.html
More Galbraith: https://graydon2.dreamwidth.org/262669.html
SIMD at least has hope of running on bare metal. I suppose the ideal is to have it adjustable: compiled to non-SIMD, using typical SIMD hardware, using implicit threads (and thus needing OS support), or using SIMD hardware with threads.
I fear the high-level web developers are in the driver's seat, which may doom rust as a systems language. I'm hesitating on rust because I would miss bitfields and various unsafe performance features. I like the opt-out safety of the "unsafe" keyword, but I want more things that I can opt out of. I want goto, computed goto, no-default switches, something like Microsoft's __assume keyword, and all the other low-level stuff you'd want for making boot loaders and high-performance kernels.
You're probably thinking of Tokio, the library for async network IO, which obviously depends on an OS. The async/await language feature compiles down to basically a state machine and has minimal runtime requirements. I'm looking forward to using it on bare-metal microcontrollers.
The largest, most well known executor is Tokio, which is built on top of OS primitives. If you're writing your own OS, you'd use those primitives.
> I fear the high-level web developers are in the driver's seat
First, I pretty fundamentally reject this as an idea, but since you don't, please do a Google Scholar search for many of the core team members. They're very much not web developers.
I mean, I like it too, but the point upthread is that you can't just suck in every nice-looking idea or you'll end up like C++.
They didn't, not individually. They were popular and uncontroversial (rather less controversial than promise apis, even). And yet...
The point is that Rust seems to be charging blindly down the same road, not that any one feature is going to blow it all up. Frankly IMHO Rust is already harder to learn for a median programmer than C++ is, even if it makes more aesthetic sense to an expert.
I'm not saying Rust is bad. I'm saying it's... becoming senselessly complicated. And that in 20 years when 60% of its amazing new features turn out to be just fashion (because they happens to everything), Rust will be "ugly" then in basically the same way that C++ is now, and we'll all be chasing some other new hotness.
2) I seriously doubt you or I have good stats on how many people who are writing production ready C++ would consider Rust hard.
To Edinburgh we go, then.
Go with its ban on generics that they hold for 10 years comes to mind. Anything else?
The idea is that Go is one language with a very specific personality: if it changes that much it just becomes another language and can as well be called something else.
Maybe that's the reason why they did hype Java to such an extent when it was a new platform, i think SUN understood that it is quite difficult to gain acceptance as a mainstream player in this game.
Shit like this is why I will never touch this language.