Rust for Pythonistas Systems Programming
Rust Syntax
Rust's Building Blocks
Coming from Python, Rust's syntax will feel both familiar and strange. You'll recognize the use of curly braces for blocks of code and the absence of semicolons at the end of most lines. But Rust is a statically typed, compiled language, which introduces some key differences right from the start.
Let's begin with variables. In Python, you can create a variable and change its value or even its type whenever you want. Rust is much stricter. It prioritizes safety, and one way it does this is by making variables immutable by default.
fn main() {
// This variable is immutable. Its value cannot be changed.
let x = 5;
println!("The value of x is: {}", x);
// To make a variable mutable, you must use the 'mut' keyword.
let mut y = 10;
println!("The initial value of y is: {}", y);
y = 20;
println!("The new value of y is: {}", y);
}
You declare variables with the
letkeyword. If you need to change a variable's value later, you must explicitly mark it as mutable withmut.
Types and Data
Rust requires you to know the type of every variable at compile time. While the compiler can often infer the type, sometimes you need to be explicit. This is a major departure from Python's dynamic typing.
Rust's data types are divided into two main categories: scalar and compound.
Scalar types represent a single value. Rust has four primary scalar types: integers, floating-point numbers, Booleans, and characters.
| Type | Description | Example |
|---|---|---|
| Integers | Whole numbers. Can be signed (e.g., i32) or unsigned (e.g., u32). The number indicates the bit size. | let num: i32 = -5; |
| Floats | Numbers with a decimal point. Available as f32 or f64. | let pi: f64 = 3.14159; |
| Booleans | true or false. | let is_rust_fun: bool = true; |
| Characters | A single Unicode character, specified with single quotes. | let initial: char = 'A'; |
Compound types group multiple values into one type. The two primary ones are tuples and arrays. Unlike Python's lists, Rust arrays have a fixed length. Tuples also have a fixed length once declared.
// A tuple with different types
let person: (&str, i32, bool) = ("Alice", 30, true);
// Accessing tuple elements by index
let name = person.0;
let age = person.1;
// An array with five elements of the same type
// The type is [i32; 5] - type; length
let numbers = [1, 2, 3, 4, 5];
// Accessing array elements by index
let first_number = numbers[0];
Controlling the Flow
Controlling the execution of your code works similarly to Python, but with some powerful additions.
An if statement in Rust is actually an expression, which means it can return a value. This allows for concise assignments that would require more lines in Python.
let number = 6;
let result = if number % 2 == 0 {
"even"
} else {
"odd"
};
println!("The number is {}", result);
Rust has several kinds of loops. A loop will run forever until you explicitly break out of it. A while loop continues as long as a condition is true. The for loop, which is very common in Python, is used to iterate over a collection.
let a = [10, 20, 30, 40, 50];
// A 'for' loop to iterate over an array's elements
for element in a.iter() {
println!("the value is: {}", element);
}
// A 'for' loop with a range, similar to Python's range()
for number in 1..4 { // This range includes 1, 2, and 3
println!("{}!", number);
}
One of Rust's most powerful control flow constructs is match. It's like a super-powered if-elif-else chain or a switch statement. It compares a value against a series of patterns and executes code based on which pattern matches. The compiler ensures you cover every possible case, which helps prevent bugs.
let grade = 'B';
match grade {
'A' => println!("Excellent!"),
'B' => println!("Good job"),
'C' => println!("You passed"),
'D' | 'F' => println!("You need to study more"),
_ => println!("Invalid grade"), // The underscore is a catch-all
}
Functions and Modules
Functions are defined with the fn keyword. Just like with variables, you must declare the types of all function parameters. You also specify the return type after an arrow ->.
A key concept in Rust is the distinction between statements and expressions. Statements perform an action and don't return a value. Expressions evaluate to a value. Most lines of code in Rust are expressions.
In a function, the final expression is automatically returned. You can use the return keyword for an early exit, but it's common to see functions end with an expression without a semicolon.
// This function takes two 32-bit integers and returns one.
fn add_numbers(x: i32, y: i32) -> i32 {
// The line 'x + y' is an expression.
// Since it's the last line, its result is returned.
// No semicolon is used here.
x + y
}
fn main() {
let sum = add_numbers(5, 7);
println!("The sum is: {}", sum);
}
To organize code, Rust uses a module system that's conceptually similar to Python's. You can group related functions, structs, and other items into modules using the mod keyword. You can then make them accessible elsewhere with the use keyword, much like Python's import.
Now, let's review what we've covered.
Time to test your knowledge.
What is the primary difference in variable handling between Rust and Python?
Which statement accurately describes arrays and tuples in Rust?
This covers the basic syntax you'll encounter. While it's a shift from Python's flexibility, Rust's strictness is designed to help you write correct and efficient code from the very beginning.
