Brainstorming about Design Principles of Rust

What is this?

A brainstorming document for thinking about what the design principles” of Rust are, as reflected in the design of the language and the standard library.

This can be a pretty broad set of things.

Feel free to add your own thoughts in the suggestions category, but please (a) do not edit what other folks wrote and (b) add your own ## section that summarizes it. It’s best if you can give some specific examples.

Also, please don’t post this link anywhere random. I want to keep this manageable.

Oh, and fair warning: I intend to come back and edit ruthlessly.

Thanks. —Niko

Examples and thoughts

Correctness first

It seems clear that an inviolable design principle of Rust is safety — it should not be possible to have undefined behavior without the unsafe keyword. But I think we’ve also geared the language more generally to guide you towards ensuring correctness in a broader sense. 

Examples:
  • exhaustive match
  • assert! runs also in optimized builds
  • overflow detection during debug builds

Readability and maintainability are important

When evaluating a potential change to Rust, we consider not only people writing Rust code, but also people reading, understanding, and maintaining Rust code as well.

When evaluating readability, we don’t just consider what the compiler is able to understand; we also consider how well human readers will understand code, both when reading in detail and when quickly reading large amounts of code.

Examples:
  • all functions include their complete signature (we don’t do “global” type inference), which limits the amount of context you need to know the types of your function arguments and return values
  • We don’t allow “duck typing”, where a function allows a parameter to have any type that appears to support the functions or fields used with that parameter. We require the definition of traits to define what the function expects.

The compiler is your friend

The Rust compiler should feel supportive, helping you figure out problems with your code before you discover them in a more hard-fought way later. The compiler isn’t trying to figure out how to understand your code at all costs; the compiler is willing to tell you that your code is wrong. This can feel surprising, depending on what tools or toolchains you’re used to using; however, developers later on express a feeling of “having a conversation” with the compiler, where they feel supported by the compiler when it flags something as wrong.

Accessibility and ergonomics

We generally want code to “read well”, with a minimum of things getting in the way of understanding. This was definitely a key factor in the stdlib design.

Examples:
  • The use of AsRef and other traits to permit functions to take many types of arguments.

Performance

Similarly, an obvious design principle in Rust is exposing and giving control over cost. But this one is subtle and complicated. You’re allowed to do expensive things, but you shouldn’t be able to “accidentally” do expensive things. And if you do something the most natural way, that should ideally also perform well; you shouldn’t have to sacrifice clarity for performance.

Examples:
  • Code that uses iterators typically compiles down to well-optimized loops, sometimes more optimized than the same code written “by hand” with indexed loops would have been.
  • Rust doesn’t implicitly store values indirectly through a pointer, even when using references; it tries to keep them on the stack, or in a register. A reference isn’t just a “smart pointer”.
  • Rust does not call .clone() automatically; you have to call it manually, which makes it visible in your code.

But counter examples:
  • copying of very large values like [T; 1024] can be done without any indication

Portability

A lesser appreciated principle, we’ve strived to make it so that Rust programs run well on as many platforms as possible. This includes Windows, Mac, and Unix-like operating systems, of course, but we’ve also tried to help ensure portability across 32- and 64-bit architectures (16-bit not so much).

Examples: