[Rust Series 3] Variables, Immutability, and Basic Data Types

한국어 버전

Once your environment is ready, you need a firm grip on variables and data types before writing real code. Rust treats every variable as immutable unless you use mut. Session 3 builds intuition for immutability and walks through scalar types plus composite types such as tuples and arrays.

New terms in this post

  1. Immutability: The default Rust behavior where variables cannot change after creation unless you explicitly make them mutable.
  2. Scalar type: A type that represents a single value at a time (i32, f64, bool, char, and so on).
  3. Tuple: A fixed-length collection that groups values in order, even if their types differ.
  4. Array: A fixed-length collection that stores values of the same type; the length is part of the type.

Core ideas

  • Variables declared with let are immutable by default; use let mut when you actually need to change them.
  • Shadowing with let creates a brand-new variable rather than modifying the old one.
  • Rust infers types, but you can annotate with : Type when clarity matters.
  • Scalar types center on integers (i32, u64), floats (f32, f64), booleans (bool), and characters (char).
  • Tuples group multiple values in order; use dot indexing or pattern destructuring.
  • Arrays store values of the same type with a fixed length, preparing you for slices later (Session 7).

Code along

1. let and mut

fn main() {
    let language = "Rust"; // immutable
    let mut version = 1;    // mutable

    println!("{} v{}", language, version);
    version += 1;
    println!("Next version: v{}", version);
}

language will trigger a compile error if you try to change it, while version can increase because it uses mut.

2. Explicit scalar types

fn main() {
    let signed: i32 = -42;
    let unsigned: u64 = 2026;
    let pi: f64 = 3.14159;
    let ready: bool = true;
    let grade: char = 'A';

    println!("signed = {signed}, unsigned = {unsigned}");
    println!("pi = {pi}, ready = {ready}, grade = {grade}");
}

Literals can carry suffixes (42u8, 1.0f32). Rust defaults to i32 for integers and f64 for floats.

3. Shadowing

fn main() {
    let steps = 5;
    let steps = steps + 1;

    println!("steps = {steps}");
}

The second let steps introduces a new variable that happens to reuse the name. Shadowing is different from mut: shadowing redeclares, while mut modifies the original binding.

4. Tuples

fn main() {
    let user: (&str, u32, bool) = ("Jisoo", 3, true);

    println!("Name: {}", user.0);
    println!("Level: {}", user.1);
    println!("Active: {}", user.2);

    let (name, level, active) = user; // destructuring
    println!("{name} / {level} / {active}");
}

Tuples stay fixed in length and allow mixed types. Get comfortable with dot indexing first, then treat destructuring as an optional convenience.

5. Arrays

fn main() {
    let scores = [95, 88, 76, 100];
    let zeros = [0; 5]; // length 5, every element is 0

    println!("First score: {}", scores[0]);
    println!("Array length: {}", scores.len());

    for score in scores {
        println!("Score: {score}");
    }
}

Array length is part of its type, so [u8; 3] and [u8; 4] are different types.

Quick summary:

  • Tuple: Can mix types, length is fixed.
  • Array: Same type only, length is fixed.
  • Both: Compile-time length.

Why it matters

  • Immutability reduces bugs from unexpected changes and forces you to mark mutable state intentionally.
  • Type clarity helps you read compiler errors and track variable lifetimes when you start learning ownership and borrowing.
  • Understanding tuples and arrays makes it easier to connect future topics like structs, slices, and iterators.

Practice in CodeSandbox

The sandbox below uses CodeSandbox's Rust starter. Move the main code into src/main.rs, then compare cargo check and cargo run so you can read the compiler feedback beside the final output.

Live Practice

Rust Practice Sandbox

CodeSandbox

Run the starter project in CodeSandbox, compare it with the lesson code, and keep experimenting.

Rust startercargoterminal
  1. Fork the starter and open src/main.rs
  2. Paste the lesson code and run cargo check plus cargo run in order
  3. Change types, values, or borrowing flow and compare the compiler feedback with the output

Rust practice here is mainly terminal-driven rather than browser-preview driven. Lessons that need multiple files or extra crates may require a bit more setup inside the starter.

Practice

  1. Run cargo new data-playground --bin, try reassigning an immutable variable in main.rs, and capture the compiler message.
  2. Write a loop that finds the maximum value in an integer array and experiment with summing values with and without mut.
  3. Create a small function that returns a tuple such as (sum, average) and destructure the result.
  4. Explain why let shadowed = 5; let shadowed = shadowed + 1; declares a new variable instead of mutating the old one.

Wrap-up

Immutability, scalar types, tuples, and arrays set your baseline for Rust's syntax. With this intuition you can track scope and state when functions and control flow appear. Session 4 moves on to defining functions with fn plus steering execution with if and loops.

💬 댓글

이 글에 대한 의견을 남겨주세요