RustYour First Program

Your First Rust Program

Every programming journey begins with a simple program. In Rust that means writing a main function, using the println! macro, and getting comfortable with how the compiler and Cargo work together to turn your source code into a running binary.

Hello, World!

Create a new project with Cargo, then open src/main.rs. It already contains the classic Hello World program:

Bash
cargo new hello_world
cd hello_world

RUST
fn main() {
    println!("Hello, World!");
}

Run it with a single command:

Bash
cargo run
   Compiling hello_world v0.1.0
    Finished dev [unoptimized + debuginfo] target(s) in 0.42s
     Running `target/debug/hello_world`
Hello, World!
The main function

Every Rust binary must have a main function — it is the program's entry point. When you run the compiled binary, execution begins at the first line of main and ends when main returns.

RUST
fn main() {
    // All code in a binary starts here.
    // The program exits when this function returns.
}
  • fn is the keyword that declares a function

  • main is the special name the runtime looks for

  • The empty parentheses () mean this function takes no parameters

  • The curly braces {} delimit the function body

println! — a macro, not a function

Notice the exclamation mark after println. That ! tells you this is a macro, not an ordinary function call. Macros are expanded at compile time and can do things ordinary functions cannot — like accept a variable number of arguments with different types.

Function

Macro

Syntax

name(args)

name!(args)

Argument count

Fixed by signature

Variable — println! accepts any number

Evaluated

At runtime

Expanded at compile time

Example

Vec::new()

println!("hi"), vec![], format!()

Note
You do not need to understand how macros are implemented to use them. Just remember: if a call ends with `!`, it is a macro. `println!`, `format!`, `vec!`, `panic!`, and `assert!` are all macros.
Format strings

println! uses format strings with curly-brace placeholders {} to embed values in output. Each {} is replaced by the next argument in order.

RUST
fn main() {
    let name = "Alice";
    let age = 30;

    // Basic placeholder
    println!("Hello, {}!", name);

    // Multiple placeholders
    println!("{} is {} years old.", name, age);

    // Named placeholders (Rust 1.58+)
    println!("{name} is {age} years old.");

    // Debug output with {:?}
    let numbers = vec![1, 2, 3];
    println!("Numbers: {:?}", numbers);

    // Pretty-printed debug with {:#?}
    println!("Numbers:
{:#?}", numbers);

    // Padding and alignment
    println!("{:>10}", "right");   // right-aligned in 10 chars
    println!("{:<10}", "left");    // left-aligned
    println!("{:^10}", "center");  // centered
    println!("{:0>5}", 42);        // zero-padded: 00042
}
Hello, Alice!
Alice is 30 years old.
Alice is 30 years old.
Numbers: [1, 2, 3]
Numbers:
[
    1,
    2,
    3,
]
     right
left
  center
00042
Compiling manually with rustc

Cargo is the standard tool, but you can also compile a single file directly with rustc, the Rust compiler:

Bash
# Compile a single file directly
rustc src/main.rs

# Produces an executable called 'main' (or 'main.exe' on Windows)
./main

# cargo run does all of this automatically and is always preferred
cargo run
Tip
Use `rustc` only for quick one-off experiments with a single file. For any real project, always use Cargo — it handles dependencies, multiple files, and build profiles correctly.
Declaring variables and printing them

Variables in Rust are declared with the let keyword and are immutable by default. The compiler infers the type from the assigned value.

RUST
fn main() {
    let greeting = "Hello";
    let target = "Rust";
    let version = 2021;

    println!("{}, {}! Edition {}", greeting, target, version);

    // Rust infers the type from the value
    let x = 5;           // i32
    let y = 3.14;        // f64
    let is_ready = true; // bool

    println!("x={}, y={:.2}, ready={}", x, y, is_ready);
}
Hello, Rust! Edition 2021
x=5, y=3.14, ready=true
Reading user input

To read a line typed by the user you use Rust's standard library std::io. Bring it into scope with a use statement, then call stdin().read_line.

RUST
use std::io;

fn main() {
    println!("What is your name?");

    let mut input = String::new();

    io::stdin()
        .read_line(&mut input)
        .expect("Failed to read line");

    // trim() removes the trailing newline character
    let name = input.trim();

    println!("Hello, {}! Welcome to Rust.", name);
}
What is your name?
Alice
Hello, Alice! Welcome to Rust.
  • use std::io brings the io module into scope

  • String::new() creates an empty, growable string on the heap

  • mut makes the variable mutable — without it you cannot call read_line

  • read_line(&mut input) appends the typed line into input, including the newline

  • .expect(...) handles the error — the program panics with that message if reading fails

  • .trim() strips the trailing \n (or \r\n on Windows) that read_line leaves in

A mini guessing game

Here is a small but complete program that combines variables, user input, type conversion, and a comparison — a taste of what Rust programs look like as they grow.

RUST
use std::io;

fn main() {
    let secret = 42;

    println!("Guess the number (between 1 and 100):");

    let mut input = String::new();

    io::stdin()
        .read_line(&mut input)
        .expect("Failed to read line");

    // Parse the string as a 32-bit signed integer
    let guess: i32 = input.trim().parse().expect("Please enter a number");

    if guess == secret {
        println!("You got it! The number was {}.", secret);
    } else if guess < secret {
        println!("Too low! The answer was {}.", secret);
    } else {
        println!("Too high! The answer was {}.", secret);
    }
}
Guess the number (between 1 and 100):
35
Too low! The answer was 42.
Note
This is a simplified single-guess version. The full guessing game in the official Rust Book adds a loop for multiple attempts and uses the `rand` crate for a random secret number — a great next step once you are comfortable with the basics.
Anatomy of a Rust source file

Rust source files share a consistent top-level structure regardless of size:

RUST
// 1. Crate-level attributes (optional, go at the very top)
#![allow(unused)]

// 2. Use statements — bring external items into scope
use std::io;
use std::collections::HashMap;

// 3. Constants and type aliases (optional)
const MAX_SIZE: usize = 100;
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

// 4. Helper function definitions
fn double(x: i32) -> i32 {
    x * 2
}

// 5. The main function (required for binaries)
fn main() {
    let result = double(21);
    println!("Result: {}", result);
}
Success
You now know how to create a Rust project, write a `main` function, use `println!` with format strings, declare variables, and read user input. These are the building blocks of every Rust program.
Tip
The Rust compiler's error messages are famously clear and helpful. When you see a compile error, read it all the way through — it usually names the exact problem and often suggests a fix.