In this article we will dig a little deeper into Rust. We will discuss the basic programming concepts but from Rust perspective. Here you will notice the similarities between Rust and the other languages but we won't delve too deep into Rust's unique traits.
β οΈ Remember!
You can find all the code snippets for this series in its accompanying repo
If you don't want to install Rust locally, you can play with all the code of this series in the official Rust Playground that can be found on its official page. (cargo commands won't run though. But you mostly won't be needing it)
β οΈβ οΈ The articles in this series are loosely following the contents of "The Rust Programming Language, 2nd Edition" by Steve Klabnik and Carol Nichols in a way that reflects my understanding from a Python developer's perspective.
β I try to publish a new article every week (maybe more if the Rust gods π are generous π) so stay tuned π. I'll be posting "new articles updates" on my LinkedIn.
Table of contents
The main function
We saw in the last article that when we create a new project with cargo
, a new file called main.rs
is created for us in the ./src/
directory and in that file, we have the "main" function:
fn main() {
println!("Hello, world!");
}
The main.rs
file and the main
function act as the entry point to our program. Your program won't run unless it has those! As opposed to Python, in which you don't need to define a main function or file.
As we can see, the main function doesn't take any arguments initially and doesn't return anything (actually it returns a unit data type "()" but more on that later).
Variables
Variables in Rust are defined by the let
and const
keywords (Yes! just like JavaScript π). They are immutable by default (their values can't be changed once set). Let's test this, create a new project called "basics" in your current directory
cargo new basics
And replace the code in main.rs
with the following and run it (remember to run a Rust code, type cargo run
):
fn main(){
let x = 5; // Variables are immutable by default
x = 6; // Error: cannot assign twice to immutable variable `x`
}
This code will error out but I'd like to highlight a very beautiful thing about Rust, its Error display method for the errors stack. It is just amazing! Rust uses colors while displaying errors, shows you where and explains why the error occurred and often provides suggestions to help solve them!
To fix this error, we will have to make our variable x a "mutable variable" by using the mut
keyword.
fn main() {
// let x = 5; // Variables are immutable by default
// x = 6; // Error: cannot assign twice to immutable variable `x`
let mut x = 5;
x = 6; // This won't error out as x is mutable now.
println!("The value of x is {x}")
}
Here we initially set x
to 5 and used the mut
keyword to tell our program that this variable is mutable then we changed its value to 6 and used the println!
macro to print its value in stdout (Rust uses curly-braces "{}" for string interpolation in the println! macro). You should see the following printed on your screen (ignore any warnings that appears for now).
The value of x is 6
The const
keyword is the other method to define variables in Rust except they are always immutable. Constants can be declared in the global scope and must be a known value/calculation at compile time (can't be calculated at runtime)
const MINUTES_IN_DAY: u32 = 1 * 24 * 60; // Valid
const WORK_HOURS_PER_WEEK = user_input; // Invalid
(u32 is the data type of the constant which stands for "unsigned" 32 bits integer)
Data Types
Rust is a statically typed language, which means that it must know the types of all variables at compile time. The Rust compiler does a good job at inferring them. But when it can't, it's the programmer's job to declare the variables type. Take a look at this example:
let guess = "42".parse().expect("Not a number"); // Error: type annotations needed
This statement will fail despite that "42" is an integer! that's because Rust has several integer types the compiler needs to know which one you mean.
Broadly, Rust has two main categories for its data types:
- Scalar: Represents a single value.
- Compound: Represents a group of multiple values.
Scalar Data Types
Integer Types
Like Python and the other languages, an integer is a whole number without a fractional component. In Rust, the integer type is written as follows:
[i/u][size in bits]
Where "i" is a signed number (can hold negative values) and "u" is an unsigned number (only positive values). The size in bits can be 8, 16, 32, 64 and 128 which determines the maximum value that the variable can hold (Integer type defaults to i32).
So, for example:
i8: 8-bit signed integer (from -128 to 127)
u8: 8-bit unsigned integer (from 0 to 255)
There are also the "isize" and "usize" types that depends on the machine architecture that the program runs on (either 32-bit or 64-bit).
Beware that if you use an integer type smaller that you have intended it to be, Integer Overflow may occur!
Floating-Point Types
Like integers, floating-point types are denoted with the letter "f" and they can be a single precision (f32) or a double precision (f64) (the default is f64)
The Boolean Type
Boolean in Rust are either true
or false
and denoted by the bool
keyword. Like in other languages, Boolean type is frequently used in condition checks.
The Character Type
In Rust, the Character type is defined by single quotes "''" and denoted by the char
keyword. It holds single character (Unicode is valid)
Compound Types
The Tuple Type
The tuple is Rust as a way to group several values together with different types into one compound type. It has a fixed size and can't grow or shrink. Like in Python, it's a comma-separated list of values
let ip_address: (u8, u8, u8, u8) = (168, 1, 1, 10);
let user: (&str, u8, char, bool) = ("Fady", 37, 'm', true);
To get the values for a Tuple, you can use pattern matching to "destruct" the tuple
let (a, b, c, d) = ip_address;
println!("The IP address is {a}.{b}.{c}.{d}")
Or you can access the tuple elements by using a period (.) followed by the index of value (zero indexed)
println!("The IP address is {}.{}.{}.{}", ip_address.0, ip_address.1, ip_address.2, ip_address.3)
A Tuple without any values has a special name, unit (remember the return value of the main function?). It is written as () and represents an empty value or an empty return type (like void in C an Java).
The Array Type
Arrays are another way to group together multiple values in a compound type but this time all the values must be of the same type. And like Tuples, Arrays are always of a fixed length. Arrays in Rust are similar to Lists in Python as they use the square brackets [] in its definition and we can access their values by providing the index inside the brackets.
let number = [1, 2, 3, 4];
println!("The second number is {}", number[1])
You can provide the type of an Array like this
[type; size]
let a: [u8; 5] = [1, 3, 4, 5, 6];
Also we can initialize an Array like this
let array = [value; size];
let a = [10; 3]; // An array of size 3 of 10 value
Rust won't let you use indexes past the size-1 of the Array. For example, the following will error out:
let hours = [1; 24];
println!("{}", hours[25])
In the next article, we will continue our discussion about the Rust basics, function and flow control. See you then π
Top comments (0)