Question
In Rust, variables are immutable by default unless they are declared with mut, which means their value cannot be changed after assignment.
If that is already the default behavior, what is the purpose of the const keyword? Are immutable variables and constants the same thing, or do they serve different roles in Rust? If they are different, how do they differ in practice?
Short Answer
By the end of this page, you will understand that Rust immutable variables and const items are not the same thing, even though neither can be changed after creation. You will learn how they differ in when values are computed, where they can be used, scope and lifetime, type requirements, and common real-world usage patterns.
Concept
Rust has two related but different ideas:
- Immutable variables: values bound to a name that cannot be reassigned
- Constants (
const): named values that are fixed at compile time
An immutable variable is created with let:
let x = 10;
This means:
xcannot be reassignedxis still a regular local variable binding- its value can come from a runtime expression
A constant is created with const:
const MAX_USERS: u32 = 100;
This means:
- the value is known at compile time
- the type must be explicitly written
- it can be declared in broader scopes, including module scope and global-like contexts
- it is intended for fixed values that never change
Why this matters
In real Rust programs, you often need both:
- immutable variables for ordinary values inside functions
Mental Model
Think of these as two different kinds of labels:
- An immutable variable is like writing a value on a sticky note during the program’s work. Once written, you do not replace the sticky note with another value.
- A constant is like printing a value in the blueprint before the building is constructed. It is part of the design itself, not something decided while the building is being used.
So both are unchangeable, but they come from different stages:
letimmutable: decided while the program runsconst: decided when the program is compiled
Another simple way to remember it:
- immutable variable = fixed during execution
- constant = fixed before execution
Syntax and Examples
Immutable variable with let
fn main() {
let name = "Alice";
println!("{}", name);
// name = "Bob"; // error: cannot assign twice to immutable variable
}
This creates a normal local variable binding. It is immutable because mut was not used.
Mutable variable with mut
fn main() {
let mut count = 1;
count = 2;
println!("{}", count);
}
Constant with const
const MAX_RETRIES: u32 = 3;
fn main() {
println!("{}", MAX_RETRIES);
}
Notice that:
Step by Step Execution
Consider this example:
const TAX_RATE: f64 = 0.1;
fn main() {
let price = 100.0;
let tax = price * TAX_RATE;
println!("Tax: {}", tax);
}
Step by step
-
The compiler sees
const TAX_RATE: f64 = 0.1;.- This value must be known at compile time.
TAX_RATEis available as a constant value.
-
The program starts running
main(). -
let price = 100.0;- A local immutable variable named
priceis created. - Its value is
100.0.
- A local immutable variable named
-
let tax = price * TAX_RATE;- The program reads
price. - The program reads the constant .
- The program reads
Real World Use Cases
When to use immutable let
Use immutable variables for ordinary values created during program execution:
- user input
- function results
- parsed data
- loop results
- temporary calculations
Example:
fn main() {
let input = String::from("42");
let number: i32 = input.parse().unwrap();
println!("{}", number);
}
number is immutable, but it depends on runtime work.
When to use const
Use const for fixed values that should be the same everywhere:
- API limits
- conversion factors
- default ports
- buffer sizes
- mathematical constants
- application-wide settings known at compile time
Example:
const DEFAULT_PORT: = ;
MAX_PACKET_SIZE: = ;
Real Codebase Usage
In real Rust projects, developers usually follow a simple pattern:
- use
letfor local values inside functions - use
constfor shared fixed values
Common patterns
1. Configuration-like fixed values
const REQUEST_TIMEOUT_SECS: u64 = 30;
const MAX_LOGIN_ATTEMPTS: u8 = 5;
These values are easy to reuse and update in one place.
2. Validation rules
const USERNAME_MIN_LEN: usize = 3;
const USERNAME_MAX_LEN: usize = 20;
fn is_valid_username(name: &str) -> bool {
let len = name.len();
len >= USERNAME_MIN_LEN && len <= USERNAME_MAX_LEN
}
3. Guard clauses using constants
const MAX_ITEMS: usize = ;
(count: ) <(), > {
count > MAX_ITEMS {
(.());
}
(())
}
Common Mistakes
1. Thinking immutable let and const are identical
They are similar in one way: neither can be reassigned.
But they differ in:
- compile-time vs runtime evaluation
- required type annotation
- where they can be declared and used
2. Trying to create a const from a runtime value
Broken example:
fn get_port() -> u16 {
8080
}
const PORT: u16 = get_port();
This fails unless the expression is allowed in a constant context.
Use an immutable variable instead if the value is only known at runtime:
fn get_port() -> u16 {
8080
}
fn main() {
let port = get_port();
println!("{}", port);
}
3. Forgetting that needs an explicit type
Comparisons
| Feature | Immutable let | const |
|---|---|---|
| Reassignable? | No | No |
| Computed at runtime? | Yes, can be | No, must be compile-time evaluable |
| Requires explicit type? | No, often inferred | Yes |
| Typical scope | Usually local bindings | Local or item-level constant definitions |
| Good for temporary values? | Yes | Usually no |
| Good for shared fixed values? | Sometimes, but less ideal | Yes |
| Naming convention | snake_case | UPPER_SNAKE_CASE |
Cheat Sheet
// Immutable variable
let x = 10;
// Mutable variable
let mut y = 20;
y = 30;
// Constant
const MAX_SIZE: usize = 1024;
Quick rules
letcreates a variable binding- variables are immutable by default
- add
mutto allow reassignment constcreates a constant itemconstmust have an explicit typeconstmust use a compile-time evaluable expression- use
UPPER_SNAKE_CASEfor constants by convention
Use let when
- the value is produced at runtime
- you need a local temporary value
- type inference is helpful
Use const when
- the value never changes
- the value is known at compile time
- you want a named fixed value reused across code
FAQ
Is an immutable variable the same as a constant in Rust?
No. Both cannot be reassigned, but an immutable variable is a normal binding created with let, while a const is a compile-time constant with stricter rules.
Why does Rust need const if variables are immutable by default?
Because immutability alone only prevents reassignment. const also expresses that a value is fixed at compile time and can be used as a named constant throughout the program.
Does const require a type in Rust?
Yes. A constant must have an explicit type annotation.
Can a const be computed from a function call?
Only if the function and expression are allowed in a constant context. In beginner-level code, assume const values should be simple compile-time expressions unless you know otherwise.
When should I use let instead of const?
Use let for local values, function results, parsed input, and other runtime computations.
Can I declare const inside a function?
Yes. A constant can be declared inside a function if it represents a fixed compile-time value.
Mini Project
Description
Build a small Rust program that calculates order totals for an online store. This project demonstrates when to use const for fixed business rules and when to use immutable let bindings for values computed while the program runs.
Goal
Create a program that uses constants for store rules and immutable variables for runtime calculations, then prints a final order summary.
Requirements
[ "Define at least two constants for fixed store rules such as tax rate or free shipping threshold.", "Use immutable let bindings for the order subtotal and calculated values.", "Compute tax and final total using those values.", "Print a clear summary showing subtotal, tax, shipping, and total." ]
Keep learning
Related questions
Accessing Cargo Package Metadata in Rust
Learn how to read Cargo package metadata like version, name, and authors in Rust using compile-time environment macros.
Associated Types vs Generic Type Parameters in Rust: When to Use Each
Learn when to use associated types vs generic parameters in Rust traits, with clear rules, examples, and practical API design advice.
Can a Struct Extend Another Struct in Rust? Composition vs Inheritance
Learn how Rust handles struct reuse without inheritance, using composition, traits, and wrapper structs with practical examples.