Hello World and Program Structure

Your First Rust Program

A Rust program is a text file of source code that the compiler turns into a native executable. The smallest useful program defines a function named main and prints a line of text.

The Program

Save this as hello.rs:

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

Compile and run it with rustc:

rustc hello.rs
./hello
Hello, world!

rustc reads hello.rs and writes an executable named hello in the current directory. The file name decides the executable name. The executable is a native binary. It does not need Rust installed to run.

file hello
hello: Mach-O 64-bit executable arm64

On Linux the same command reports an ELF executable, and on Windows rustc produces hello.exe.

Use -o to choose a different output name:

rustc -o greeting hello.rs
./greeting
Hello, world!

Anatomy of the Program

Part Meaning
fn Keyword that starts a function definition
main The name of the entry point. Program execution begins in this function. Every executable program needs exactly one
() The parameter list. main has no parameters
{ } The function body. Rust requires braces around every function body
println! A macro from the standard library that prints text followed by a newline
"Hello, world!" A string literal. Text between double quotes
; Ends the statement

Rust style uses four spaces for each indentation level, not tabs. The formatter rustfmt applies this automatically.

Why println! Has an Exclamation Mark

The ! after a name means the name is a macro, not a function. A macro is expanded by the compiler into other code before the program is compiled. println! is a macro because it accepts a variable number of arguments and checks the format string at compile time. A regular Rust function cannot do either.

Leaving out the ! is an error:

// ❌ does not compile
fn main() {
    println("Hello, world!");
}
error[E0423]: cannot find function `println` in this scope
 --> e2.rs:2:5
  |
2 |     println("Hello, world!");
  |     ^^^^^^^ not found in this scope
  |
  = note: a macro named `println` exists in another namespace
help: use `!` to invoke the macro
  |
2 |     println!("Hello, world!");
  |            +

Where println! Is Defined

println! is defined in the standard library, std. It is available in every program without an import because the standard library's macros and its most common types are brought into scope automatically.

Execution Order

The program starts at main and runs its statements from top to bottom. Other functions run only when called. The order in which functions are written in the file does not matter.

fn main() {
    println!("1. main starts");
    greet();
    println!("3. main ends");
}

fn greet() {
    println!("2. inside greet");
}
1. main starts
2. inside greet
3. main ends

greet is defined after main and is still callable from it. When main returns, the program ends.

A function that is defined but never called compiles, but the compiler warns about it:

fn main() {
    println!("Hello");
}

fn helper() {}
warning: function `helper` is never used
 --> p5.rs:4:4
  |
4 | fn helper() {}
  |    ^^^^^^
  |
  = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default

A warning does not stop compilation. The executable is still produced and runs.

Printing Text

println! prints its text and then a newline. print! prints the text without a newline. println!() with no arguments prints only a newline.

fn main() {
    println!("Hello, world!");
    println!("Welcome to Rust.");
    print!("No newline here. ");
    print!("Same line.\n");
    println!();
    println!("Tab:\tcolumn");
    println!("Quote: \"Rust\"");
    println!("Braces: {{}}");
    println!("Two lines
in one string");
}
Hello, world!
Welcome to Rust.
No newline here. Same line.

Tab:	column
Quote: "Rust"
Braces: {}
Two lines
in one string

Escape sequences inside a string literal start with a backslash: \n is a newline, \t is a tab, \" is a double quote, \\ is a backslash. A string literal may also span several lines, and the line break becomes part of the text. Because { and } mark placeholders in the format string, a literal brace is written twice: {{ and }}.

Placeholders

Each {} in the format string is replaced by one argument, in order. A variable name inside the braces uses that variable directly.

fn main() {
    let language = "Rust";
    let year = 2015;
    println!("Hello, {}!", language);
    println!("{} was first stable in {}.", language, year);
    println!("{language} was first stable in {year}.");
    println!("{0} {1} {0}", "a", "b");
    println!("{name} is {age}", name = "Ferris", age = 3);
}
Hello, Rust!
Rust was first stable in 2015.
Rust was first stable in 2015.
a b a
Ferris is 3

The number of placeholders must match the number of arguments. The compiler checks this:

// ❌ does not compile
fn main() {
    println!("Hello, {}!");
}
error: 1 positional argument in format string, but no arguments were given
 --> p3.rs:2:22
  |
2 |     println!("Hello, {}!");
  |                      ^^

The first argument of println! must be a string literal. A variable cannot be used as the format string.

Standard Output and Standard Error

println! and print! write to standard output. eprintln! and eprint! write to standard error, which is the stream for diagnostics and errors.

fn main() {
    println!("to stdout");
    eprintln!("to stderr");
}
./f2 2>/dev/null
to stdout
./f2 >/dev/null
to stderr

Redirecting one stream leaves the other on the terminal, so the two can be separated.

Running with Cargo

Real projects use cargo, which creates the project layout, calls rustc, and runs the result.

cargo new hello_world
cd hello_world
cargo run
   Compiling hello_world v0.1.0 (/path/to/hello_world)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.21s
     Running `target/debug/hello_world`
Hello, world!

cargo new generates a project that already contains the Hello World program:

hello_world/
├── Cargo.toml
└── src/
    └── main.rs

src/main.rs:

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

Cargo.toml:

[package]
name = "hello_world"
version = "0.1.0"
edition = "2024"

[dependencies]

cargo run builds the program if needed, places the executable in target/debug/, and runs it. The Compiling and Finished lines go to standard error, so only Hello, world! is written to standard output.

Common Errors

Missing Semicolon

// ❌ does not compile
fn main() {
    println!("Hello, world!")
    println!("again");
}
error: expected `;`, found `println`
 --> e1.rs:2:30
  |
2 |     println!("Hello, world!")
  |                              ^ help: add `;` here
3 |     println!("again");
  |     ------- unexpected token

error: aborting due to 1 previous error

No main Function

A file that defines no main cannot be compiled into an executable. A misspelled name, including a different capitalization such as Main, produces the same error.

// ❌ does not compile
fn hello() {
    println!("Hello, world!");
}
error[E0601]: `main` function not found in crate `e3`
 --> e3.rs:3:2
  |
3 | }
  |  ^ consider adding a `main` function to `e3.rs`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0601`.

Single Quotes Instead of Double Quotes

Single quotes delimit a char, which holds exactly one character. Text uses double quotes.

// ❌ does not compile
fn main() {
    println!('Hello, world!');
}
error[E0762]: unterminated character literal
 --> e4.rs:2:28
  |
2 |     println!('Hello, world!');
  |                            ^^^
  |
help: if you meant to write a string literal, use double quotes
  |
2 -     println!('Hello, world!');
2 +     println!("Hello, world!");
  |

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0762`.

Unclosed Brace

// ❌ does not compile
fn main() {
    println!("Hello, world!");
error: this file contains an unclosed delimiter
 --> e5.rs:2:32
  |
1 | fn main() {
  |           - unclosed delimiter
2 |     println!("Hello, world!");
  |                               ^

error: aborting due to 1 previous error