Introduction to Rust

Halvor Fladsrud Bø
5 min readMar 13, 2018

--

This is an article about the basics of Rust. It is intended for people with some expirience programming, but it will not go too deep into the details, just to get you up and going in Rust. If you do not already have Rust installed:

A basic program in Rust

To make a new project in rust, you can use cargo. In the beginning:

 cargo new helloworld

This will generate a lot of files, including a “src” folder. This is where you put your “main.rs” file, with the following content:

fn main() {  println!("Hello, world!");}

Your folder structure should look like this:

helloworld
-> src
-> main.rs
...
...

To run the program (make sure you are inside the project folder):

cargo run

You should now se “Hello, world!” in the console. Congratulations on your first Rust program!

Variables

The first part of learning any programming language is variables. Before going to deep, let’s look at the following example:

let x = 5;

If you know basic JS, you know that this is how you declare a variable in that language. Funnily enough, this is also valid Rust code and it declares a new variable like you would expect. So you would assume this would work:

x += 1;

It will not. This is one of Rust’s safety features. Every variable is immutable, unless you explicitly state that its not.

let mut x = 5;
x += 1;
println!("{:?}", x); // this is how you print out a variable

This will compile just fine and print the number 6.

let mut x = 5; 
let mut x = 6;
let mut x = "Hello";

As will this.

But why? The Rust compiler will imply the type based on the usage. In this case x is a i32 which is the default for whole numbers. At the same time you have multiple other datatypes for whole numbers, like i64, u32 and u64. The u stands for unsigned, meaning that it can’t be a negative number. If you want to be explicit with your types, just add the following:

let x : i64 = 5;

Know you know the basics of variables in Rust, before we move on, let’s quickly look at arrays:

let mut a = vec![1, 2, 3]; 
a.push(4); // is only possible if a is mutable
a.pop();

This is one way to declear a vector in Rust, and it’s equal to the “C++ Vector”, “Java ArrayList” or a normal list in JS or Python. There are normal arrays as well, but vectors are, in my opinion, a better place to start.

Loops

Cool, you now know how to make an array in Rust. To use an array, you probably will want a loop. A for-loop is decleared like this:

for n in a {  println!(n);}

You migth expect me to show you the while-loop in Rust, but there I’m sorry to disapoint, but there is no while-loop in Rust. Instead you have:

for n in 1..5 { // 1, 2, 3, 4
...
}
loop { // loops forever
... // needs to contain a "break"
}

Iterators can give you even more functionality.

let b = vec!["foo", "bar"];
for (i, n) in b.iter().enumerate() { ... } // (0, "foo"), (1, "bar")

Iterators have other uses as well, like running a function for every element in an array:

let c = vec![1, 2, 3];
let d : Vec<i32> = c.iter()
.map(|x| x + 1) // apply this to every element
.collect(); // get the result

The |x| part may be a bit confusing, but this is just giving every element in the array a name and apply the following function to that name.

Control statements

The most basic control statement, the if-statement is a bit different from most other languages. You don’t need the parentacies.

if a.len() > 100 {  println!("That is a really long array!");}

The other frecuently used statement in Rust is the match statement, with is the same as the switch-statement in most other languages.

match x {  1 => { ... }, // If x is 1
2 => { ... }, // If x is 2
3 => { ... }, // If x is 3
_ => { ... }, // If x is the default
}

Match-statements are important in Rust because they are used for error handling, so make sure you understand them. A nice thing in Rust is that most code blocks evaluate to some value, so you could say something like:

let v = match x {  5 => {"A"},
4 => {"B"},
3 => {"C"},
2 => {"D"},
1 => {"E"},
_ => {"F"}
}

This would assign the variable v to whatever the block the the match statement hits, evaluates to. Notice that it says “{“A”}”, which is the same as saying “{ return “A” }”.

Functions

Functions in Rust are a bit special, because of how the return works. Like mentioned above, the blocks (“{}”) evaluate to something. This means that this function would return v.

fn foo() -> u32 {   ... // some code that defines a u32 named v
v
}

You do not have to follow this pattern. You can, as mentioned above, add return, but this makes no difference in practice. To set the input of a function just add:

fn bar(n : i32) { ... }

This is also part of how Rust can imply the type of variables. It knows the return type and input and output of every function, if n goes into bar, it needs to be an i32, or the code will not compile.

Error handling

To understand error handling in Rust we need to look at two datatypes: “Result” and “Option

pub enum Result<T, E> {
Ok(T),
Err(E),
}

Above you can see how a Result is defined. It is an enum which means it can have one of these values. Ok represent the expected Result. Let us say you are reading from a file, the expected Result would be the data from the file. Err, on the other hand, represents an error. These are most commonly use

Further reading

This is the official book about Rust, and will teach you everything you need to know.

--

--

Halvor Fladsrud Bø

Software Engineer. Interested in security, distributed systems and databases. Using this as a scratchpad.