Rust — Syntax
The shape of Rust code
fn main() {
let greeting = "Hello";
println!("{}, world!", greeting);
}
Reading the pieces:
| Piece | Meaning |
|---|---|
fn | defines a function |
main | the entry point name (fixed) |
let | declares a variable |
println! | macro call — ! is mandatory for macros |
; | ends a statement |
Statements vs expressions — the core distinction
Rust is expression-oriented. This single idea explains half the language's style.
Statement — performs an action, returns nothing:
let x = 5; // statement: binding a value
Expression — evaluates to a value:
5 + 3 // expression → 8
"hello" // expression → that string
Function bodies and blocks return their last expression — without a semicolon:
fn add(a: i32, b: i32) -> i32 {
a + b // ← no semicolon = this value is returned
}
Add a semicolon and you've turned it into a statement returning nothing — the compiler then complains about the missing return type:
fn broken(a: i32, b: i32) -> i32 {
a + b; // ❌ statement discards the value
}
That stray semicolon error will bite you exactly once. Then it never will again.
Types are annotated after the name
Opposite of Java/C order:
let count: u32 = 10; // type AFTER variable
let price: f64 = 9.99;
fn area(width: f64, height: f64) -> f64 // arrow for return type
Macros — the exclamation marks
Names ending in ! are macros — code that writes code at compile time:
println!("plain"); // print with newline
print!("no newline");
println!("{} scored {}", name, score); // {} are placeholders
println!("{:?}", some_vector); // Debug formatting for complex types
vec![1, 2, 3] // vec! creates vectors
Why macros instead of functions? println! accepts any number of arguments of any printable type — regular Rust functions can't be that flexible.
Shadowing — redeclare, don't just reassign
let again on the same name creates a NEW binding:
let spaces = " "; // string slice
let spaces = spaces.len(); // now a number — same name, new binding ✓
This is idiomatic Rust: transform a value and keep the meaningful name, without inventing spaces2. Different from mut — shadowing can even change the type; mut cannot.
Comments
// line comment
/* block
comment */
/// doc comment — generates docs & shows in editor hover
/// Adds two numbers together.
fn add(a: i32, b: i32) -> i32 { a + b }
Triple-slash doc comments attach to the item below and power cargo doc.
Formatting conventions
- 4-space indentation (
cargo fmtenforces it automatically) - snake_case for variables/functions:
user_count - UpperSnakeCase for constants:
MAX_POINTS - PascalCase for types:
UserProfile
Run cargo fmt after every session — the whole ecosystem matches this style because nobody argues with the formatter.
Mini Practice
- Write
fn square(x: i32) -> i32; break it by adding a semicolon; read the error. - Print a formatted line with two
{}placeholders. - Shadow a string into its length like the example above.
- Try
//,/* */and///comments; runcargo doc --open. - Run
cargo fmton deliberately messy code.
Next: output →
Related Topics
Frequently Asked Questions about Syntax
What is Syntax in Rust?
Syntax is a fundamental concept in Rust. This lesson explains it step by step with clear examples, making it easy for beginners to understand.
How do I learn Syntax?
Start by reading the explanation above, then try the code examples. Practice by modifying the examples and experimenting with different values. Hands-on practice is the best way to learn Syntax.
Why is Syntax important in Rust?
Syntax is essential for Rust development. Understanding this concept will help you write better code and solve real-world problems more effectively.