Chapter 4: Rust Syntax
Rust Syntax now, which is the next logical step after installation, introduction, and getting started.
Today we’re going deep into how Rust looks and feels when you write code — the grammar, the rules, the little symbols that make Rust unique. I’ll explain it like a real teacher sitting with you: slowly, with many examples, analogies from everyday life (and Indian context 😄), comparisons to other languages you might know (Python, Java, C++), and code you can copy-paste + run right now.
Rust syntax is clean but strict. It borrows ideas from C/C++ (curly braces, semicolons), but adds modern safety features (no null, pattern matching everywhere, powerful macros). Most of the “weird” parts exist to enforce ownership & borrowing at compile time — that’s the magic that makes Rust safe without slowing down.
Let’s break it down section by section — start simple, build up.
1. Basic Structure — Hello World & Entry Point
Every Rust binary starts with main():
|
0 1 2 3 4 5 6 7 8 |
fn main() { println!("Namaste Hyderabad! Learning Rust syntax today 🚀"); } |
- fn = keyword for function (like def in Python, void in C++)
- main() = special name — Rust always looks for this as starting point
- { } = curly braces define block/scope (same as C-family)
- println! = macro (notice the !) — prints with newline. Macro = code that writes code (super powerful, like templates)
- ; = ends almost every statement (forget it → compiler yells nicely)
Run with cargo run — output:
|
0 1 2 3 4 5 6 |
Namaste Hyderabad! Learning Rust syntax today 🚀 |
2. Variables & Mutability (Biggest difference from Python)
Variables are immutable by default — huge safety win!
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
fn main() { let temperature = 32; // immutable, type inferred as i32 // temperature = 35; // ERROR! cannot assign twice to immutable let mut score = 100; // mutable because we use mut score = score + 50; // OK let city = "Hyderabad"; // &str (string slice) let population: u64 = 10_000_000; // explicit type + underscore for readability // Shadowing: redeclare same name (very common & useful) let city = "Hyderabad, Telangana"; // shadows previous city println!("City: {}, Population approx: {}", city, population); } |
- let = declare variable (no var, const separate)
- mut = only if you plan to change it (prevents accidental bugs)
- Types inferred automatically most times (great productivity)
- Shadowing ≠ mutation — it’s a new variable with same name (cleans up code)
3. Data Types & Primitives (Quick overview)
Rust has strong, static typing — but inference hides it often.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
let integer: i32 = -42; // signed 32-bit let unsigned: u64 = 18446744073709551615; // max u64 let float: f64 = 3.14159265359; // double precision let boolean = true; // or false let character: char = 'హ'; // Unicode! (Telugu 'Ha' works fine) let emoji = '🦀'; // Ferris the Rust crab // Tuples (fixed size, heterogeneous) let person = ("Webliance", 25, true); // ( &str, i32, bool ) let (name, age, is_learning) = person; // destructuring // Arrays (fixed size, same type) let days = [ "Monday", "Tuesday", "Wednesday" ]; // [&str; 3] let temperatures: [f32; 5] = [28.5, 30.0, 32.1, 29.8, 31.2]; |
No null — use Option<T> instead (we’ll see later).
4. Strings — Two kinds (very important)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
let literal = "Hello"; // &str — immutable borrow of string literal let owned: String = String::from("Owned string"); // heap-allocated, mutable let mut greeting = String::from("Namaste"); greeting.push_str(", world!"); // mutates owned String println!("Literal: {}, Owned: {}", literal, greeting); |
- &str = view/slice (cheap, common in function args)
- String = owns data (can grow, like Python str but explicit)
5. Functions & Parameters (Clean & explicit)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
fn add(a: i32, b: i32) -> i32 { // types mandatory for params & return a + b // no ; = implicit return (expression) } fn greet(name: &str, age: u32) { println!("Hello {}, you are {} years old!", name, age); } fn main() { let sum = add(10, 25); greet("Webliance", 25); println!("Sum: {}", sum); } |
- Return type after ->
- Last line without ; = return value
- &str = borrow (no ownership transfer)
6. Control Flow — if, loop, match (match is king!)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
let temp = 38; if temp > 35 { println!("Very hot day in Hyderabad! 🔥"); } else if temp > 30 { println!("Summer weather ☀️"); } else { println!("Nice!"); } // loop (infinite until break) let mut count = 0; loop { count += 1; if count == 5 { break; } println!("Count: {}", count); } // while let mut number = 3; while number != 0 { println!("{}!", number); number -= 1; } // for (best for collections) for i in 1..=5 { // 1 to 5 inclusive println!("Learning Rust day {}!", i); } |
Match — Rust’s most loved feature (like super-powered switch)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
let food = "biryani"; match food { "biryani" => println!("Hyderabadi favorite! 🍛"), "idli" | "dosa" => println!("South Indian classic"), "pizza" => println!("Italian twist"), _ => println!("Something else"), // _ = catch-all } |
Exhaustive — compiler forces you to handle all cases!
7. Ownership Syntax in Action (The heart — quick intro)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
let s1 = String::from("hello"); let s2 = s1; // move — ownership transferred // println!("{}", s1); // ERROR — s1 invalid now let s3 = String::from("world"); let len = calculate_length(&s3); // borrow (no move) println!("Still have: {}", s3); // OK fn calculate_length(s: &String) -> usize { s.len() } |
- No & = move ownership
- & = immutable borrow
- &mut = mutable borrow (only one at a time!)
8. Structs & Enums (Custom types)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
struct User { name: String, age: u32, active: bool, } let user1 = User { name: String::from("Webliance"), age: 25, active: true, }; enum Message { Quit, Move { x: i32, y: i32 }, // struct-like Write(String), // tuple-like ChangeColor(i32, i32, i32), } let msg = Message::Write(String::from("Hello")); |
Quick Tips from 2026 Teacher
- Indentation = 4 spaces (official)
- snake_case for variables/functions, CamelCase for types
- ! = macro
- ? = error propagation (in functions returning Result)
- Compiler errors are your friend — very detailed & helpful
This covers ~80% of day-to-day syntax. The rest (traits, generics, lifetimes ‘a, async/await) come later.
Want to focus next on one part?
- Match & patterns in detail?
- How ownership syntax feels in bigger examples?
- Struct + impl (methods)?
- Or run a bigger code example together?
Tell me — I’m your Rust guru today! 🦀🚀
