Basic syntax
Let's have a look at some basic Rust syntax. Just like in the previous chapters, you can run these code snippets in your browser or in your local data-with-rust
folder.
Variables
Variables are the smallest building blocks of your programs. They usually reference some data of some sort.
There are many ways of declaring variables in Rust. Usually, this is done by prefixing the variable name with let
, const
or static
. A mutable
variable is a variable that might change at some point, an immutable
variable is a variable that can't change while the program is running.
fn main() { let x = 5; // defining an immutable variable let mut y = 6; // defining a mutable variable const Y2K: i32 = 2000; // const variables require a known type static mut POTATOES: u32 = 0; // This is a mutable static variable }
In Rust, variables are immutable by default, meaning that their values cannot be changed after being assigned. For example, if you try to change the x
value after assignment, you'll get an error.
To declare a mutable variable, you need to use the mut
keyword, like it's done for y
.
Now the natural question is: what is the difference between let
, const
and static
?
- If the variable isn't meant to be changed, there is not much difference between the three.
const
is essentially a constant variable, it cannot be changed, it is immutable. Which would be expected from something that is constant. During compile time, it is inlined/copied to every location it is used which has some efficiency gains.static
is essentially a reference to a fixed location in memory, making it roughly similar to a global variable. It can be mutated but with some conditions (usingunsafe
code blocks).
For now, I recommend to only use let
, but for the sake of completeness, this is how you would change things:
fn main() { let x = 5; // defining an immutable variable let mut y = 6; // defining a mutable variable y = 7; // changing a mutable variable's value println!("The value of y is {}", y); const Y2K: i32 = 2000; // const variables require a known type static mut POTATOES: u32 = 0; // This is a mutable static variable unsafe { POTATOES = 1; println!("The value of POTATOES is {}", POTATOES); } }
Using unsafe is usually not recommended. If the logic of your program allows it, try to avoid unsafe
.
Control flow
Now that we know how to initialise variables, we can start doing stuff with them. Most of the time, these things fall into using if/else
or loops
. I will not cover all the edge cases, only the ones that are relevant in 90% of the cases. For a full coverage you can refer to Rust by Example.
If/Else & match
Let's start with if/else
. The syntax is similar to Python. In this case we want to check if a condition expressed as Rust code evaluates to true or not and do something in each case.
fn main() { let x = 5; if(x < 0){ println!("Negative"); } else if(x > 0) { println!("Positive"); } else { println!("Zero"); } }
The parenthesis are not mandatory, but I strongly recommend them. Explicit is better than implicit.
Another way of rewriting the above is by using match
:
fn main() { let x = 5; match x { n if n < 0 => println!("Negative"), n if n > 0 => println!("Positive"), _ => println!("Zero"), // Match requires all possible outcomes to be handled } }
Both match
and if/else
are pretty similar, however, if/else
statements are more appropriate for simple boolean conditions, while match
statements are more appropriate for complex conditions that require pattern matching (matching types for example) and multiple possible outcomes.
Loops
Writing code involves iterating through data and this is done using loops. There are many ways to loop through things in Rust: for
, while
and loop
.
The simplest way of a loop is doing something a number of times over:
fn main() { for n in 1..10 { println!("N={}", n) } }
The most used way however, is to iterate through a list of things:
fn main() { let languages = ["Python", "Rust", "C++"]; for name in languages.iter() { println!("I love {}", name); } }
Next up are loop
& while
. I like to think of loop as a "while True", they are pretty similar.
fn main() { let mut count = 0; loop { count += 1; if(count < 10){ // continue iterating, similar to Python println!("Steady..."); continue; } if(count == 10){ println!("Bye bye!"); // Exit the loop, similar to Python break; } } }
The while
version would look like this:
fn main() { let mut count = 0; while count < 10 { count += 1; println!("Steady..."); if(count == 10){ println!("Bye bye!"); break; } } }
There's much more to this, but for now we can create variables, loop through stuff and start becoming quite productive with Rust.
Next let's have a look at data types, which represents the bread and butter of data engineering.