Rust Syntax
Rust has a syntax that draws inspiration from C-family languages while introducing unique concepts for memory safety and concurrency. Understanding Rust's syntax is essential for writing correct and idiomatic Rust code.
Basic Program Structure
Every Rust program starts with a main
function:
fn main() {
println!("Hello, world!");
}
Functions are declared with the fn
keyword, followed by the function name, parameters in parentheses, and the function body in curly braces.
Statements vs Expressions
Rust is an expression-based language, which means most constructs return a value.
Statements
Statements perform an action and do not return a value:
fn main() {
let x = 5; // Statement: variable declaration
let y = 10; // Statement: variable declaration
}
Expressions
Expressions evaluate to a value:
fn main() {
let x = 5;
// Block expression
let y = {
let x = 1;
x + 1 // Expression: no semicolon
};
println!("y is: {}", y); // y is: 2
}
Variables and Immutability
By default, variables in Rust are immutable:
fn main() {
let x = 5;
// x = 6; // This would cause a compile error
// Make variables mutable with 'mut'
let mut y = 5;
y = 6; // This is allowed
println!("x: {}, y: {}", x, y);
}
Variable Shadowing
Rust allows variable shadowing, where you can declare a new variable with the same name:
fn main() {
let x = 5;
let x = x + 1; // Shadows the previous x
let x = x * 2; // Shadows again
println!("x is: {}", x); // x is: 12
// Shadowing can change the type
let spaces = " ";
let spaces = spaces.len(); // Now spaces is a number
}
Constants
Constants are declared with const
and must have their type annotated:
const MAX_POINTS: u32 = 100_000;
fn main() {
println!("Max points: {}", MAX_POINTS);
}
Functions
Function syntax includes parameters, return types, and return values:
fn main() {
let result = add(5, 3);
println!("5 + 3 = {}", result);
let value = get_value();
println!("Value: {}", value);
}
// Function with parameters and return type
fn add(x: i32, y: i32) -> i32 {
x + y // Expression return (no semicolon)
}
// Function returning a value using 'return' keyword
fn get_value() -> i32 {
return 42;
}
// Function with no return value (returns unit type ())
fn print_hello() {
println!("Hello!");
}
Comments
Rust supports several types of comments:
fn main() {
// Single-line comment
/*
* Multi-line comment
* can span multiple lines
*/
/// Documentation comment for the following item
/// These are used to generate documentation
let x = 5;
//! Inner documentation comment
//! Documents the enclosing item
}
/// Documentation comment for a function
/// This will appear in generated docs
fn documented_function() {
// Regular comment inside function
}
Operators
Rust provides familiar operators with some unique behaviors:
Arithmetic Operators
fn main() {
let a = 10;
let b = 3;
println!("Addition: {}", a + b); // 13
println!("Subtraction: {}", a - b); // 7
println!("Multiplication: {}", a * b); // 30
println!("Division: {}", a / b); // 3 (integer division)
println!("Remainder: {}", a % b); // 1
}
Comparison Operators
fn main() {
let a = 5;
let b = 10;
println!("Equal: {}", a == b); // false
println!("Not equal: {}", a != b); // true
println!("Less than: {}", a < b); // true
println!("Greater than: {}", a > b); // false
println!("Less or equal: {}", a <= b); // true
println!("Greater or equal: {}", a >= b); // false
}
Logical Operators
fn main() {
let a = true;
let b = false;
println!("AND: {}", a && b); // false
println!("OR: {}", a || b); // true
println!("NOT: {}", !a); // false
}
Assignment Operators
fn main() {
let mut x = 10;
x += 5; // x = x + 5
println!("x after += 5: {}", x); // 15
x -= 3; // x = x - 3
println!("x after -= 3: {}", x); // 12
x *= 2; // x = x * 2
println!("x after *= 2: {}", x); // 24
x /= 4; // x = x / 4
println!("x after /= 4: {}", x); // 6
}
Control Flow
If Expressions
fn main() {
let number = 6;
if number % 4 == 0 {
println!("Number is divisible by 4");
} else if number % 3 == 0 {
println!("Number is divisible by 3");
} else if number % 2 == 0 {
println!("Number is divisible by 2");
} else {
println!("Number is not divisible by 4, 3, or 2");
}
// if as an expression
let condition = true;
let value = if condition { 5 } else { 6 };
println!("Value: {}", value);
}
Loops
fn main() {
// Infinite loop
let mut counter = 0;
loop {
counter += 1;
if counter == 5 {
break;
}
println!("Counter: {}", counter);
}
// While loop
let mut number = 3;
while number != 0 {
println!("{}!", number);
number -= 1;
}
println!("LIFTOFF!!!");
// For loop
let a = [10, 20, 30, 40, 50];
for element in a.iter() {
println!("Value: {}", element);
}
// For loop with range
for number in 1..4 {
println!("{}!", number);
}
}
Pattern Matching
The match
expression is fundamental to Rust:
fn main() {
let value = 3;
match value {
1 => println!("One"),
2 => println!("Two"),
3 => println!("Three"),
_ => println!("Something else"),
}
// Match with multiple patterns
let x = 1;
match x {
1 | 2 => println!("One or two"),
3..=5 => println!("Three through five"),
_ => println!("Something else"),
}
}
Macros
Macros are called with !
and can take various argument patterns:
fn main() {
// println! macro
println!("Hello, world!");
// Formatting
let name = "Rust";
println!("Hello, {}!", name);
// vec! macro to create vectors
let v = vec![1, 2, 3, 4, 5];
// panic! macro for unrecoverable errors
// panic!("This will crash the program");
}
Attributes
Attributes provide metadata about code:
// Outer attribute (applies to the following item)
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 1, y: 2 };
println!("{:?}", p);
}
// Conditional compilation
#[cfg(target_os = "windows")]
fn platform_specific() {
println!("This only compiles on Windows");
}
// Allow certain warnings
#[allow(dead_code)]
fn unused_function() {
// This function won't trigger a warning
}
Keywords
Rust has several categories of keywords:
Current Keywords
// These are currently used keywords
// as, break, const, continue, crate, else, enum, extern,
// false, fn, for, if, impl, in, let, loop, match, mod,
// move, mut, pub, ref, return, self, Self, static, struct,
// super, trait, true, type, unsafe, use, where, while
Reserved Keywords
// These are reserved for future use
// abstract, become, box, do, final, macro, override,
// priv, typeof, unsized, virtual, yield
String Literals
Rust provides several ways to write strings:
fn main() {
// Regular string literal
let s1 = "Hello, world!";
// Raw string literal (no escape sequences)
let s2 = r"C:\Users\name\file.txt";
// Multi-line string
let s3 = "This is a
multi-line string";
// Raw multi-line string
let s4 = r#"
This is a raw
multi-line string
with "quotes"
"#;
// Byte string
let bytes = b"Hello";
println!("{}", s1);
}
Numeric Literals
Rust supports various numeric literal formats:
fn main() {
// Decimal
let decimal = 42;
// Hexadecimal
let hex = 0xff;
// Octal
let octal = 0o77;
// Binary
let binary = 0b1111_0000;
// Byte (u8 only)
let byte = b'A';
// Visual separators
let million = 1_000_000;
// Type suffixes
let x = 42u32;
let y = 3.14f64;
println!("Decimal: {}, Hex: {}, Binary: {}", decimal, hex, binary);
}
Semicolons and Expressions
Understanding when to use semicolons is crucial:
fn main() {
// Statements end with semicolons
let x = 5;
let y = 10;
// Expression blocks return the last expression
let z = {
let inner = x + y;
inner * 2 // No semicolon = returned value
};
println!("z is: {}", z); // z is: 30
// Adding semicolon makes it a statement
let w = {
let inner = x + y;
inner * 2; // Semicolon = returns ()
};
// w is now () (unit type)
println!("w is: {:?}", w); // w is: ()
}
Code Organization
Modules
// Declare a module
mod my_module {
pub fn public_function() {
println!("This is public");
}
fn private_function() {
println!("This is private");
}
}
fn main() {
my_module::public_function();
// my_module::private_function(); // Error: private
}
Use Statements
use std::collections::HashMap;
use std::fmt::{Display, Debug};
fn main() {
let mut map = HashMap::new();
map.insert("key", "value");
}
Common Syntax Patterns
Variable Declarations
fn main() {
let x = 5; // Immutable binding
let mut y = 5; // Mutable binding
let (a, b) = (1, 2); // Destructuring tuple
let [first, second] = [1, 2]; // Destructuring array
}
Function Calls
fn main() {
let result = function_name(arg1, arg2);
let result = object.method_name(arg1);
let result = Type::associated_function(arg1);
}
Error Handling
fn main() {
// Using Result with match
match std::fs::read_to_string("file.txt") {
Ok(contents) => println!("{}", contents),
Err(error) => println!("Error: {}", error),
}
// Using ? operator (in functions that return Result)
// let contents = std::fs::read_to_string("file.txt")?;
}
Understanding Rust's syntax is the foundation for writing effective Rust code. The language's expression-based nature, strict type system, and ownership rules all work together to create a powerful and safe programming environment.