Rust is a language of rules, some of them stricter than most. Here's what you need to know before writing your first program in Rust.
Few languages are as highly esteemed by developers as Rust, which promises robust memory safety without compromising speed. Rust also has a steep learning curve, though, and some of its programming concepts can feel like a barrier to knowing it well.
As an aid to the aspiring Rustaceans, here are four key concepts of the Rustosphereโthe kinds of things a more experienced Rust developer might wish they knew from the beginning. Having a grasp on these concepts will give you a head start when you start writing programs with Rust.
Also see: Rust tutorial: Get started with the Rust language.
All Rust variables are immutable by default
Rustโs first assumption about any variable you create is that it is immutable by default.
If you say let x=1;, the variable x is bound to the value 1 for the lifetime of the program. Any variable that might change has to be specifically declared as such with the mut (for โmutableโ) keyword; e.g., let mut x=1;.
This โSir, may I?โ strategy is a deliberate design choice for Rust, even though at first it might seem ornery. But hereโs the rationale: Immutable entities in a program are easier to reason about and make guarantees aboutโespecially guarantees about memory safety, which is Rustโs big selling point. If the compiler knows some element in a given scope doesnโt change once itโs assigned, most of the memory management issues around that element evaporate.
Another rationale for this design decision is that it forces the programmer to think about what actually does need to change in a program. Taking input from the user, for instance, or reading from a file, are operations that must be handled mutably. But you can leave many other operations as immutable with no loss of functionality.
With advanced Rust data structures, such as reference-counted Cell objects, you can use immutability to your advantage. For example, this immutable singly-linked list has no mutable components; every change to the list essentially involves creating a new list, albeit one that doesnโt waste memory copying because the existing list data can be re-used.
Rust has strict rules of ownership
Rustโs memory management model relies on a strict concept of ownership. Any value in Rust can only be modified by one thing at a timeโits owner. Think of how thereโs only one baseball at a time in a baseball game, and only one player at a time handling it. That owner can change over the lifetime of a program, in the same way the ball is thrown between players. But Rust refuses to allow a value to have more than one owner at once, just as weโd rather not have baseball players tussling over the ball.
Also see: Rust memory safety explained.
Itโs possible for something to be read by more than one thing at once in Rust, but not possible for something to be changed by more than one thing at once. There can be multiple shared references or โborrowsโ to something at once, for the sake of read-only access, but there can be only one exclusive (read-write) reference to something at once.
The net effect is that Rust programmers must pay close attention to what data can be modified at what time. Itโs important to understand that a program that breaks these rules wonโt just crash; in Rust, it wonโt even compile. Thatโs because the rules of ownership are not optional. (More on this later.)
Another net effect is that Rust memory management at runtime is not handled by way of a dedicated garbage collection system, as with C# or Go. Things like dynamic reference counting are possible, but theyโre more akin to C++โs smart pointersโalbeit with better runtime controls for thread safety.
A final note is that ownership does not make it impossible for Rust programs to have data race conditions. Data access across threads, for instance, still needs to be serialized with primitives like locks or queues to avoid unpredictable in-program behavior. But Rust does make it impossible for code to have many common memory-access-error bugs that plague other languages.
Borrow checking enforces Rustโs ownership rules
A big part of Rustโs appeal is that its ownership rules forbid representing states that might cause common memory errors. These problems arenโt caught at runtime but at compile time, so they never enter production.
A downside is that Rust programmers spend a fair amount of time, at least when theyโre first learning the language, figuring out how to appease the compiler. Program behaviors that might have passed unnoticed in other languages (and possibly caused memory errors at runtime) cause the Rust compiler to stop cold.
The other big downside is that you canโt opt out of this behavior. You canโt toggle off Rustโs borrow-checking behavior the way you could, say, disable a code linter for another language. It makes for better software in the long run. But the immediate cost is a language thatโs both slower to learn and slower to iterate in.
Rust does allow you to fence off parts of your code with the unsafe keyword, and lift some restrictions, like the ability to dereference a raw pointer. But unsafe does not turn off borrow checking entirely, or anything like that. Itโs for taking code with certain behaviors you donโt want casually used (again, like dereferencing a raw pointer) and โgatingโ it for safety.
Also see: Safety off: Programming in Rust with โunsafeโ.
Rust editions support backward-compatibility
Itโs been almost a decade since Rust 1.0 came out, and the language has evolved aggressively since then. Many new features came along, not all of them backward-compatible with older versions of the language. To keep these changes from making it hard to maintain code in the long term, Rust introduced the concept of language editions.
Editions, which are published roughly every three years, provide a guarantee of compatibility. By and large, features added to Rust are supported going forward. Breaking changesโones that are backward-incompatibleโare added to the next edition rather than the current one.
Once you choose an edition, youโre not tied to it forever. You can migrate your code to future editions by way of both automated tooling and manual inspection. But thereโs also a good chance the changes you are interested in will be backward-compatible with your older code, and thus available without migrating editions.


