Getting Started with Rust Using Rustlings — Part 3: If and Primitive Types

Jesse Verbruggen
8 min readFeb 13, 2023

--

Part 3: Ifs & Primitive Types

In my last part, I went over Functions in Rust. This is Part three of my miniseries on Rust. If you want to read the previous part, you can get to it by following the link to my previous article below.

In this series, I am going over my process of learning the syntax of Rust with Rustlings. In my first Part, I explain how to install Rust and Rustlings. So if you haven’t yet done that, please navigate there to follow along.

If Statements

Before we start the exercises, let me quickly explain what an if statement is for anyone unfamiliar.

An if statement will check if the statement validates as true or false. The code in the if statement's block runs only if the statement is true. Any statement can be given as long as the result is a boolean.

if1.rs

pub fn bigger(a: i32, b: i32) -> i32 {
// Complete this function to return the bigger number!
// Do not use:
// - another function call
// - additional variables
}

This function comes with comments which ask us to complete the function to return the bigger number. It takes two i32 variables which we will compare using the operator to find the biggest number and return it.

We will create an if statement to check which numbers are bigger and return it.

pub fn bigger(a: i32, b: i32) -> i32 {
if (a >= b) {
return a;
} else {
return b;
}
}

In this case, we used the >= operator which checks whether the value of a is bigger or equal to b. If that’s not the case, we have our else block to handle the other scenario and return b.

if2.rs

// Step 1: Make me compile!
// Step 2: Get the bar_for_fuzz and default_to_baz tests passing!
pub fn foo_if_fizz(fizzish: &str) -> &str {
if fizzish == "fizz" {
"foo"
} else {
1
}
}

Let’s tackle this problem step by step. For the first step, making this compile, we want to return a string in both scenarios. Let’s follow the standard “foobar” structure and return “bar” within the else brackets.


pub fn foo_if_fizz(fizzish: &str) -> &str {
if fizzish == "fizz" {
"foo"
} else {
"bar"
}
}

Great, this code compiles. That means we can move on to step two and look at the test cases to figure out what else we need to do.

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn foo_for_fizz() {
assert_eq!(foo_if_fizz("fizz"), "foo")
}

#[test]
fn bar_for_fuzz() {
assert_eq!(foo_if_fizz("fuzz"), "bar")
}

#[test]
fn default_to_baz() {
assert_eq!(foo_if_fizz("literally anything"), "baz")
}
}

The test case for default_to_baz is failing. One way we can ensure this is working is by adding another check for “fuzz”, which will then return “bar” and have the default scenario output “baz”. That will change the code to the following.

pub fn foo_if_fizz(fizzish: &str) -> &str {
if fizzish == "fizz" {
"foo"
} else if fizzish == "fuzz" {
"bar"
}
else {
"baz"
}
}

Now we can see both test cases working, and we learned how to chain if statements in a block. Following up on this is a quiz. I will leave it to you to solve yourself without help as we move on to Primitive Types.

Primitive Types

What are primitive types? In Rust, there are “primitive” and “non-primitive” types.

Primitive types are the most basic building blocks of data that we can use to represent simple concepts. In Rust these are:

  • Booleans: These represent a logical value of true or false. These are stored as 8-bit values, aka 1 byte.
  • Characters: These represent individual letters, numbers, symbols, or special characters and are enclosed in single quotes. ''
  • Integers: These represent numbers stored as an integer value. They can be both signed and unsigned. Signed means they use one bit to tell whether the number is positive or negative. Unsigned numbers can use this extra bit to represent even larger numbers. An i32 type represents a signed integer that takes up 32 bits and can represent numbers up to 2³¹ in size, both positive and negative. An u32 type represents an unsigned integer that can be up to 2³² in size. These are always parsed as positive numbers but you can interpret them in code as negative numbers with some additional logic.
  • Floating-point numbers: These represent both single-precision (f32) and double-precision (f64) floating-point numbers used to represent decimal values.
  • Arrays and Slices: These are used to store a fixed or variable number of values of the same type in contiguous memory locations.
  • Tuples: These are used to group values of different types into a single compound value.
  • Pointers and references: These are used to point to a memory address or reference a value stored in memory.

These exercises only go over Booleans, Characters, Arrays, Slices and Tuples. I expect that the other primitive types will be used in the upcoming exercises, where I will go over them.

Booleans: primitive_types1.rs

fn main() {
// Booleans (`bool`)

let is_morning = true;
if is_morning {
println!("Good morning!");
}

let // Finish the rest of this line like the example! Or make it be false!
if is_evening {
println!("Good evening!");
}
}

For this exercise, we can make use of the ! operator. Which will flip a boolean value to its opposite. We’ll create a variable called is_evening that is the opposite of is_morning.

fn main() {
// Booleans (`bool`)

let is_morning = true;
if is_morning {
println!("Good morning!");
}

let is_evening = !is_morning;
if is_evening {
println!("Good evening!");
}
}

This way, our code will still function even if we change the value of is_morning to false.

Characters: primitive_types2.rs

fn main() {
// Characters (`char`)

// Note the _single_ quotes, these are different from the double quotes
// you've been seeing around.
let my_first_initial = 'C';
if my_first_initial.is_alphabetic() {
println!("Alphabetical!");
} else if my_first_initial.is_numeric() {
println!("Numerical!");
} else {
println!("Neither alphabetic nor numeric!");
}

let // Finish this line like the example! What's your favorite character?
// Try a letter, try a number, try a special character, try a character
// from a different language than your own, try an emoji!
if your_character.is_alphabetic() {
println!("Alphabetical!");
} else if your_character.is_numeric() {
println!("Numerical!");
} else {
println!("Neither alphabetic nor numeric!");
}
}

As my explanation explained, a character is an individual letter, number, symbol, or special character enclosed in single quotes. As the exercise says, this can be an emoji, so let’s try that out.

fn main() {
// Characters (`char`)

// Note the _single_ quotes, these are different from the double quotes
// you've been seeing around.
let my_first_initial = 'C';
if my_first_initial.is_alphabetic() {
println!("Alphabetical!");
} else if my_first_initial.is_numeric() {
println!("Numerical!");
} else {
println!("Neither alphabetic nor numeric!");
}

let your_character = '🚀';
if your_character.is_alphabetic() {
println!("Alphabetical!");
} else if your_character.is_numeric() {
println!("Numerical!");
} else {
println!("Neither alphabetic nor numeric!");
}
}

The execution of our code tells us that an emoji is neither alphabetic nor numeric, as we would expect. As you can see, a character in Rust can take on multiple shapes.

Arrays: primitive_types3.rs

fn main() {
let a = ???

if a.len() >= 100 {
println!("Wow, that's a big array!");
} else {
println!("Meh, I eat arrays like that for breakfast.");
}
}

Onto an entirely new concept, arrays. Arrays in Rust are a collection of things. An array is defined using square brackets [] . In our brackets, we can pass initializers and a size like this [1; 100]. Where 1 is our initializer and 100 is our array size. Arrays are not mutable in size, which is why we must specify a size in the declaration, or the array can only hold as many values as you specify, separated by commas.

fn main() {
let a = [1; 100];

if a.len() >= 100 {
println!("Wow, that's a big array!");
} else {
println!("Meh, I eat arrays like that for breakfast.");
}
}

Slices: primitive_types4.rs

#[test]
fn slice_out_of_array() {
let a = [1, 2, 3, 4, 5];

let nice_slice = ???

assert_eq!([2, 3, 4], nice_slice)
}

In contrast to Arrays, Slices can be resized to hold more or fewer values. To take a slice from an array, we can use the & reference to the array and specify bounds to get only [2, 3, 4] from the original array. The bounds are specified as two numbers with .. in between. The first number is the index of the first item, while the second is one more the index of the final item we need. In this case, [1..4]. If we put this all together, we get the following.

#[test]
fn slice_out_of_array() {
let a = [1, 2, 3, 4, 5];

let nice_slice = &a[1..4];

assert_eq!([2, 3, 4], nice_slice)
}

Tuples: primitive_types5.rs and primitive_types6.rs

fn main() {
let cat = ("Furry McFurson", 3.5);
let /* your pattern here */ = cat;

println!("{} is {} years old.", name, age);
}

In the exercise above, cat is a tuple holding two values. We can extract these values into two separate variables to solve this exercise. It’s the reverse of what is done in the tuple assignment.

fn main() {
let cat = ("Furry McFurson", 3.5);
let (name, age) = cat;

println!("{} is {} years old.", name, age);
}

primitive_types6.rs

#[test]
fn indexing_tuple() {
let numbers = (1, 2, 3);
// Replace below ??? with the tuple indexing syntax.
let second = ???;

assert_eq!(2, second,
"This is not the 2nd number in the tuple!")
}

In this exercise, a tuple is defined with three values. We only want to get the second value which we can do with the indexing syntax. With the indexing syntax, we must add a dot and the index of the value we need.

#[test]
fn indexing_tuple() {
let numbers = (1, 2, 3);

let second = numbers.1;

assert_eq!(2, second,
"This is not the 2nd number in the tuple!")
}

Alright, that’s all the exercises I will go over right now. I hope you enjoyed following along with me. As I complete the exercises, join me to start your journey into Rust. The next part about Vectors is out now for you to read.

If I helped you learn more about Rust or you found this interesting, please clap 👏 and share this article to help more people find these articles.

Let’s help each other learn and grow! Peace out✌️

--

--