In Rust, variables behave somewhat differently than in many other programming languages. Here’s a breakdown of how variables work in Rust, focusing on their unique features.
Default Mutability Philosophy
Rust’s choice to make variables immutable by default ensures that developers think carefully about mutability. It aligns with Rust’s overarching philosophy of safety and prevents unintended side effects in code.
Because variables in Rust are immutable by default, it means that once a value is assigned to a variable, you cannot change it unless explicitly allowed.
let x = 5; // Immutable variable
x = 6; // this will throw an error
To make a variable mutable, you must explicitly declare it as mut
:
let mut x = 5; // Immutable variable
x = 6; // this is now allowed
The idea is that this provides for safer, more predictable code. In reality, that means you define constants by default, and new coders may just make everything mutable with the mut
keyword.
Type Inference
Rust has strong, static typing, but the compiler can often infer the type of a variable from its context, making type annotations optional.
let x = 10; // Compiler infers `x` is an `i32`
let x: i64 = 10; // Explicitly typed
Category | Data Type | Description |
---|---|---|
Scalar Types | i8 , i16 , i32 , i64 , i128 | Signed integers with various bit sizes. |
u8 , u16 , u32 , u64 , u128 | Unsigned integers with various bit sizes. | |
f32 , f64 | Floating-point numbers (single and double precision). | |
bool | Boolean type (true or false ). | |
char | Unicode character (4 bytes). | |
Compound Types | (T1, T2, ...) | Tuples that can group multiple values of different types. |
[T; N] | Arrays with fixed size. | |
Collection Types | Vec<T> | Growable vectors (dynamic arrays). |
Text Types | String | Growable UTF-8 encoded string. |
&str | String slice (borrowed view of a string). | |
Pointer Types | &T | Immutable reference. |
&mut T | Mutable reference. | |
Box<T> | Heap-allocated value. | |
Option/Result | Option<T> | Represents an optional value (Some or None ). |
Result<T, E> | Represents success (Ok ) or error (Err ). | |
Custom Types | struct , enum | Used to define user-defined data structures. |
Variable Shadowing
Rust allows shadowing, which means you can declare a new variable with the same name as an existing one. The new variable completely “shadows” the old one. Most languages will not let you do this.
let x = 5;
let x = x + 1; // Shadows the previous `x`
Shadowing is different from mutability because it allows you to reuse the variable name while potentially changing the type:
let spaces = " "; // A string
let spaces = spaces.len(); // Now an integer
Constants
Rust also supports constants, which are always immutable and must have their type explicitly defined.
Constants differ from variables in that they are evaluated at compile-time, not runtime, and their values cannot be changed.
You will need to use const
to declare a constant rather than let
.
const MAX_POINTS: u32 = 100_000;
Ownership and Borrowing in Variables
Rust variables are tied closely to its ownership model, which governs memory safety. That is you can own a piece of data, and thus transfer it, you can borrow it, and you can clone it.
Ownership Transfer: When you assign one variable to another, ownership of the value is typically transferred unless the type implements the Copy
trait.
let s1 = String::from("hello");
let s2 = s1; // Ownership moves to `s2`; `s1` is now invalid
println!("{}", s1); // Error: `s1` is no longer valid
Now, this is in part because a string is an object. If you use a primative, it works just fine.
let s1 = 73
let s2 = s1;
println!("{}", s1); // No error here!
Cloning: If you want to create a new copy of the value (especially for types like String
), you use the .clone()
method.
let s1 = String::from("hello");
let s2 = s1.clone(); // Creates a deep copy of `s1`
println!("{}", s1); // Valid: `s1` is still accessible
Borrowing: You can borrow a variable’s value by passing it as a reference.
let s1 = String::from("hello");
let len = calculate_length(&s1); // Borrowing `s1` -- assume this is a UDF
println!("The length of '{}' is {}.", s1, len); // `s1` is still accessible
Working with Variables in Rust was originally found on Access 2 Learn