Variables & Basic Types
Chapter 1 named memory safety as Rust's whole reason for existing. This chapter's first rule is a direct expression of that philosophy: variables are immutable by default — the opposite of what Go, JavaScript, and Python all do.
Immutability by Default
let x = 5; creates an immutable binding — attempting to reassign it is a compile error, not something that fails at runtime or silently succeeds. Mutability has to be opted into explicitly:
This is a deliberate design choice, not an arbitrary restriction: most variables, in practice, are never reassigned after being set — making immutability the default means the compiler can catch an accidental reassignment as a mistake, rather than a program silently doing something the programmer didn't intend.
Scalar Types
Rust's basic scalar types are explicit about size and signedness, unlike Go's simpler int/uint or JavaScript's single Number type:
Compound Types
A tuple groups a fixed number of values of mixed types; an array holds a fixed number of values of the same type. Both have a size fixed at compile time — a dynamically growable list is Vec<T>, covered in Chapter 8.
Type Inference & Explicit Annotations
Rust infers types in most cases — let x = 5; is inferred as i32 without an annotation, the same convenience Go's := provides. An explicit annotation is only needed when the inferred default isn't what's wanted, or when the type genuinely can't be inferred from context alone.
Shadowing
Rust allows re-declaring a variable with the same name using let again — this creates a brand-new binding that shadows the previous one, and can even change its type entirely. This is genuinely different from Go, where redeclaring a name in the same scope with := is usually a compile error unless at least one new variable is being introduced.
The second let input doesn't mutate the original string variable — it creates a separate variable that happens to share the name, and the earlier &str version is no longer accessible from this point in the scope onward.
| Concept | Go | Rust |
|---|---|---|
| Default mutability | Mutable by default | Immutable by default (mut opts in) |
| Type inference | := infers the type | let infers the type |
| Redeclaring the same name | Compile error (with :=, unless a new var is added) | Allowed — shadowing, can change type |
| Fixed-size list | Array (rarely used directly) | [T; N] — fixed-size, same type |
let again each time — a plain reassignment (input = ... with no let) is mutation, and requires mut, an entirely different mechanism. Shadowing is also scope-limited: once a shadowed variable's block ends, the outer variable (if one exists) becomes visible again, unaffected by whatever the inner shadow did. Confusing shadowing with mutation is a common early mix-up — they look superficially similar but follow completely different rules.
Coding Challenges
Declare an immutable variable holding your age as a u8, then write a second line that attempts to reassign it. Run cargo run, note the exact compiler error, then fix it using mut.
📄 View solutionCreate a tuple holding a product's id (i32), price (f64), and category initial (char). Print each field individually using tuple indexing (.0, .1, .2).
📄 View solutionUsing shadowing, take a variable holding the string "100", and shadow it twice: first into an i32, then into that number multiplied by 2. Print the final value, and explain why this required let each time rather than plain reassignment.
📄 View solutionChapter 2 Quick Reference
- let x = ... — immutable by default; reassigning without
mutis a compile error - let mut x = ... — explicitly opts into mutability
- Scalar types: i32/u32/i64/u64 (explicit width + signedness), f32/f64, bool, char (4-byte Unicode scalar)
- Tuples — fixed size, mixed types, indexed with
.0/.1/.2 - Arrays — fixed size, same type,
[T; N]; a growable list isVec<T>(Ch.8) - Shadowing — re-declaring with
letcreates a new binding, can change type, scope-limited - Shadowing ≠ mutation — shadowing needs
leteach time; mutation needsmut, no newlet - Next chapter: Ownership — move semantics, one owner at a time, and why this eliminates the need for a garbage collector