Maybe this article will dispel many of your misconceptions about Rust.
Rustacean is the name given to users of the Rust programming language. Therefore, the “Rustacean Principle” can be seen as the principle that the official Rust team used to create the Rust language from the perspective of the user of the Rust language.
Rustacean is a hybrid of Rust + crustacean. Crustacean is a crustacean, so the Rust community uses crabs as mascots of the Rust language, calling them Ferris, Chinese skyscraper crabs. It is clear that the mascot was not officially designated by Rust, and was originally published by Karen on the rustacean.net website as a concept art drawing of Ferris.
The Rustacean Principles are a 2021 project initiated by Rust Language Team Leader Niko, which seeks to enumerate the principles that govern Rust design and how the community works. Niko said the principles are not officially published on behalf of Rust, but only his own opinion. The focus of establishing principles is to try to develop the principles better and use them in the team.
Although these are experimental principles, they have been consistently discussed and endorsed within Rust officials. As can also be seen from Niko’s latest blog, Rust Asynchronous trait Part 8 and Talk about “Soul of Rust”, this principle should already be in use.
Last year, Niko’s article on Rust principles also sparked some controversy in the community. Steve Klabnik, a member of the Rust core team, believes that Niko’s principles are modeled after the Amazon Principles, so implies that Amazon is somewhat responsible for the development of Rust. Amazon employs multiple Rust maintainers and contributors, but it’s just one of many companies with employee involvement. The opposite is the case with Mara Bos, head of the Rust library team, who says Steve’s view is “just nonsense.” Looking back at the principles after a year, it is not as outrageous as Steve Klabnik said, and the content of the Rust principles does guide the development and contribution of the Rust Project, so that everyone knows where to go.
I intend to write this article today to learn the principles behind the creation process of the Rust language with readers and friends to further understand Rust’s design philosophy. It’s worth noting that most of the details of the Rustacean principles are not written by Niko, so there are a lot of details here that I’ve added.
The Rustacean Principles are divided into two main parts:
This principle, in general, serves Rust’s overall goals. Rust’s overall goal is to be a language that empowers everyone to build reliable and efficient software.
This part of the principle includes:
By “the soul of Rust,” Niko refers to the struggle between these key principles of the Rust team – especially the trade-offs between productivity, diversity and transparency.
Specifically, reliability means that Rust code that is guaranteed to be secure avoids undefined behavior. Type safety is a key element of reliability. Type safety is not a verbal advice, but is managed by the compiler. But type safety increases the complexity of the language and makes it more difficult to learn Rust. To that end, the Rust team worked very misinformation and documentation to mitigate the learning costs associated with this complexity. It was because of these costs that the Rust team imposed some restrictions on what Rust’s type system was trying to achieve.
For example, runtime checks are used for certain types of error conditions. Instead of trying to prove that the index is in scope, it checks expressions like vec[i] to ensure that i < vec.len(). Proving i< vec.len() at compile time increases the complexity of the type system, so the team chose not to do so, although this would cost some reliability but increase productivity.
Another example is allowing users to escape the complexity of type systems using Unsafe code. For example, Safe Rust cannot express a bidirectional linked list, but it can be implemented with Unsafe Rust. However, users are also expected to be able to encapsulate (security abstraction) their Unsafe code to show the world a secure interface. This is contrary to the productivity perception of Unsafe code writers (it’s more complicated to think about how to encapsulate things), but it has a lot of benefits for reliability in the rest of the world.
Rust, on the other hand, doesn’t hide error conditions and encourages a clear list of all possibilities (or an admission that something is ignored). For example, Rust requires users to provide detailed match matching branches, forcing users to consider all situations. Doing so helps improve the reliability of your Rust code, but this comes at the cost of reducing user productivity. So, it’s a trade-off.
Error handling is a good case for trade-offs. For a long time in the history of programming languages, error handling has generally been the use of exceptions. Exception handling increases productivity for users. But anomalies hide the control flow, it is difficult for users to reason, and in practice it is full of problems, which is extremely detrimental to reliability. Rust uses the first method of return enumeration in functional languages to force users to consider the way errors are handled, which helps reliability. Later introduced? Operators, which make it easier for users to propagate errors, are productivity gains while ensuring that error paths are still visible to the user and are not completely ignored.
Rust draws on the zero-cost abstraction of the C++ community. Bjarne, the father of Cpp, defines the zero-cost abstraction as: “What you don’t use, you don’t pay for. And further: What you do use, you couldn’t hand code any better”。 Zero-cost abstraction means that users can write code using the advanced abstraction capabilities provided by the language, while the compiler generates high-performance code for developers through optimization, and furthermore, optimizes the redundant and useless code to optimize the useful code more efficiently.
This performance test is not intended to step on the C#/Java language, but only to highlight the performance of Rust’s zero-cost abstraction ability. As you can see, Rust provides a very elegant and Java/C# equivalent high-level iterator abstraction without compromising the performance of your code.
This is also a point that is misunderstood by many people, who believe that the zero-cost abstraction promoted by Rust is 100%. But it’s actually very difficult to guarantee a 100% zero-cost abstraction, and Rust is full of trade-offs in this regard.
Rust’s developers believe that zero-cost abstraction is not only the pursuit of zero cost and optimal performance, but also the focus on improving the user experience, because this is what abstraction means. There are only a handful of zero-cost abstractions in the Rust language that meet this standard, and these are listed by withoutboats in his blog:
Other features have not had much success, some examples of this:
Why is it so hard to abstract 100% of the zero-cost? Because there are so many factors to consider for Rust. In addition to providing users with a good experience of abstraction, there are trade-offs to transparency and diversity, which are factors that interfere with compiler optimization.
Another misconception many people have about Rust is that code implemented with Rust must perform well. But the results of practice are likely to break their perception. Developers need to consider the following issues when using Rust code:
Because Rust’s zero-cost abstraction does not guarantee the best performance of user-written Rust code.
Rust tools are designed to provide developers with a beautiful, fluid experience. An example is how compilers can provide high-quality error messages that not only try to indicate errors, but also teach users how the Rust language works and provide useful advice on how to fix their code. Recently, Rust officially launched a multilingual translation program for diagnostic information, and everyone is welcome to contribute.
For tools like cargo, this is reflected in the careful CLI design that makes “simple things simple.” Useful third-party plugins based on Cargo are becoming more abundant.
Productivity can conflict with reliability, high performance, so there are trade-offs here. Consider the several features listed above for the success of Rust’s zero-cost abstraction, such as the ownership mechanism. Many people think that ownership mechanisms affect productivity because this secure memory management approach is relatively new and not so quickly accepted. But switching to the GC language, developers do not have this mental burden, and productivity naturally increases. But developers skilled with Rust will not be affected.
What is the way to make Rust extremely productive?
The official Rust team places a high value on transparency. Transparency is the underlying control that Rust gives users, but it’s important to note that it automatically boosts performance.
But transparency exposes the underlying control details, making diversity and productivity diminished. For example, the repr attribute, and then the Box
Transparency conflicts with diversity and productivity, and there are careful trade-offs when designing language features.
Rust also attaches importance to diversity, and diversity means versatility, which means that Rust can do upper-level applications and low-level system development.
The official goal is to expose all core system functions to the Rust program in some way, even though it can be difficult to access or use them correctly. Instead of wanting Rust users to feel like they have to choose C or another language, they should be able to use Unsafe Rust to do their job. Features like “inline assembly” follow this line of thinking as well.
A typical example I can think of is that Rust moved Error trait into the core so that std and no_std error handling could be unified.
This section provides some of the following guidelines to help the Rust core team and community contributors work together:
Niko’s blog also mentions a Goldilocks principle, which is interesting.
The principle comes from an English fairy tale, The Blonde and the Three Bears.
It is about a blonde girl who sneaks into the bear’s house and finds three bowls of porridge, three chairs and three beds, some cold and some hot; Chairs are hard and soft; The beds had big and small ones. After she tasted and tried it all, she chose the bowl of porridge that was not lukewarm or hot, the chair that was not hard or soft, the bed that was not big or small, because the bowl of porridge, the chair, and the bed were all “just right” for her, and the principle of this choice was called the “blonde principle”.
The “Goldilocks Principle” is applied in various fields, such as developmental psychology, economics, communication science, medicine and astrobiology, communication, and so on.
If this principle is applied to how things are viewed (new), then it becomes a very good thinking tool. By using this principle, you can avoid thinking black and white about things that exist in the world, such as the Rust language, such as someone. The world is not the extremes of good and bad, black and white, it still has an intermediate state.
There may be a lot of detail missing from this article, but in general I think the Rustacean principles and what Niko calls the soul of Rust should be made clear. The Rust language isn’t perfect, but it evolves over and over again and again between these principles. We can be imperfect, and we can’t be perfect, but we can’t but pursue perfection. Thanks for reading.
Rust programming language: https://rust-lang.org/
Projects initiated by Niko in 2021: https://github.com/nikomatsakis/rustacean-principles
Rust asynchronous trait Part 8: https://smallcultfollowing.com/babysteps/blog/2022/09/18/dyn-async-traits-part-8-the-soul-of-rust/
Talk about “Rust Soul”: https://smallcultfollowing.com/babysteps/blog/2022/09/19/what-i-meant-by-the-soul-of-rust/
Zero Cost Abstractions: https://github.com/mike-barber/rust-zero-cost-abstractions
withoutboats in his blog: https://boats.gitlab.io/blog/post/zero-cost-abstractions/