05 Rustlings Primitive Types Solution

Rust has a couple of basic types that are directly implemented into the compiler. In this section, we'll go through the most important ones.

Further information

Additional reading resources can be found here:

primitive_types1.rs

// primitive_types1.rs
// Fill in the rest of the line that has code missing!
// No hints, there's no tricks, just get used to typing these :)

// I AM NOT DONE

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!");
    }
}

In the code comments it's clear that we are looking at Booleans which are a type that is either true or false . With that in mind, let's take a look at what the errors are to make sure we have a complete picture.

primitive_trype1.rs errors

⚠️  Compiling of exercises/primitive_types/primitive_types1.rs failed! Please try again. Here's the output:
error: expected identifier, found keyword `if`
  --> exercises/primitive_types/primitive_types1.rs:16:5
   |
16 |     if is_evening {
   |     ^^ expected identifier, found keyword

error: expected one of `:`, `;`, `=`, `@`, or `|`, found `is_evening`
  --> exercises/primitive_types/primitive_types1.rs:16:8
   |
16 |     if is_evening {
   |        ^^^^^^^^^^ expected one of `:`, `;`, `=`, `@`, or `|`

error: aborting due to 2 previous errors

These errors point to the fact that the if_evening portion of the code is incomplete, in fact it's almost like the compiler is saying "wait a minute, shouldn't you let me know what is_evening is?" instead, it found the keyword if.

primitive_types1.rs solution

// primitive_types1.rs
// Fill in the rest of the line that has code missing!
// No hints, there's no tricks, just get used to typing these :)

// I AM NOT DONE

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

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

    let is_evening = false; // easy enough to complete this as above
    if is_evening {
        println!("Good evening!");
    }
}

This works out pretty quickly, by just defining what is_evening is with let is_evening = false; we get the code to compile and it prints:

🎉 🎉  The code is compiling! 🎉 🎉

Output:
====================
Good morning!

====================

Great, but what would happen if we make the both of them true? Let's find out.


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

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

    let is_evening = true; // making both `true`
    if is_evening {
        println!("Good evening!");
    }
}

This time we get a slightly different print, but the code still compiles.

🎉 🎉  The code is compiling! 🎉 🎉

Output:
====================
Good morning!
Good evening!

====================

Simple enough, we're on to the next one.

primitive_types2.rs

// primitive_types2.rs
// Fill in the rest of the line that has code missing!
// No hints, there's no tricks, just get used to typing these :)

// I AM NOT DONE

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!");
    }
}

Again we have no hints, so we have pay close attention to the code and the errors we get from the Rust compiler.

⚠️  Compiling of exercises/primitive_types/primitive_types2.rs failed! Please try again. Here's the output:
error: expected identifier, found keyword `if`
  --> exercises/primitive_types/primitive_types2.rs:24:5
   |
24 |     if your_character.is_alphabetic() {
   |     ^^ expected identifier, found keyword

error: expected one of `:`, `;`, `=`, `@`, or `|`, found `your_character`
  --> exercises/primitive_types/primitive_types2.rs:24:8
   |
24 |     if your_character.is_alphabetic() {
   |        ^^^^^^^^^^^^^^ expected one of `:`, `;`, `=`, `@`, or `|`

error: aborting due to 2 previous errors

This looks similar to what we got in our previous exercise, essentially telling us that the your_character part of the code is incomplete.

Let's go through the comments in the code.

  • The first comment in fn main() tells us that we are looking at Characters
  • Second comment makes sure that we notice the use of single quotes on the line let my_first_intial = 'C'
  • The third comment tells us to finish the let statement using the example we've seen but use our favorite character.

Looking at how the rest of the code is written, to make it easier on us, we can use the your_character variable that is being used, but of course as the errors tells us, is undefined. Let's define it.


fn main() {

    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 = '🦀'; // we define it as a crab emoji just for fun.
    if your_character.is_alphabetic() {
        println!("Alphabetical!");
    } else if your_character.is_numeric() {
        println!("Numerical!");
    } else {
        println!("Neither alphabetic nor numeric!");
    }
}

primitive_types3.rs

// primitive_types3.rs
// Create an array with at least 100 elements in it where the ??? is.
// Execute `rustlings hint primitive_types3` or use the `hint` watch subcommand for a hint.

// I AM NOT DONE

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.");
    }
}

Here we have clear instructions on what we need to do, and we even have a hint if we need it. But let's look at the errors first:

⚠️  Compiling of exercises/primitive_types/primitive_types3.rs failed! Please try again. Here's the output:
error: expected expression, found `?`
 --> exercises/primitive_types/primitive_types3.rs:8:13
  |
8 |     let a = ???
  |             ^ expected expression

error: aborting due to previous error

Short and sweet, we see that on line 8, there's an expected expression where we need to replace the ??? question marks. So what kind of expression do we need, well the instructions tell us it should be an array with at least 100 elements in it.

Array Review

Let's quickly review the ways that we can create an array in Rust.

One of the simplest ways we can create an array in Rust is by using the [] square brackets and listing the elements inside of it, like this:

let a = [1, 2, 3, 4, 5, 6];

We can also define the array's type by using another set of square brackets before the definition of the array, in a similar what that you define types when defining variables.

let a: [i32; 6] = [1, 2, 3, 4, 5, 6];

These are both valid, but remember in our exercise we have to make an array with at least 100 elements in it -- there must be a better way. Luckily there is, as long as we want the array to contain the same value we can initialize the array like this:

let a = [3;5];

This will give us an array called a with that will contain 5 elements with their value set to 3 this is the same as writing

let a = [3, 3, 3, 3, 3];

but clearly in a more concise way.

primitive_types3.rs Solution

Now that we have this information fresh in our minds we can clearly see the winning solution for our problem of having 100 elements in an array. Let's do it.


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

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

We get this back from the compiler:

Output:
====================
Wow, that is a big array!

====================

and just to confirm, what if we make the array less than 100 elements? Let's just change the 100 to 99 like this: let a = [1; 99];

Output:
====================
Meh, I eat arrays like that for breakfast.

====================

primitive_types4.rs

// primitive_types4.rs
// Get a slice out of Array a where the ??? is so that the test passes.
// Execute `rustlings hint primitive_types4` or use the `hint` watch subcommand for a hint.

// I AM NOT DONE

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

    let nice_slice = ???

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

primitive_types4.rs error

⚠️  Compiling of exercises/primitive_types/primitive_types4.rs failed! Please try again. Here's the output:
error: expected expression, found `?`
  --> exercises/primitive_types/primitive_types4.rs:11:22
   |
11 |     let nice_slice = ???
   |                      ^ expected expression

error: aborting due to previous error

In this piece of code we see almost the exact same set-up having to define a variable, in this case we need to define it with a slice. Remembering a slice is essentially how we refer to part of an array, remember when we had the 100 element array in the previous exercise, or even a bigger array that had thousands of elements. Well what if only needed to access part of the array? We would use a slice in this scenario so let's do it.

We define a slice by using &[ ] the & makes it like a reference, so we don't have ownership of the array, but we can go deeper into this another time, let's just define this slice.

The error code is also very straightforward, we're being told that we're missing an expression.

primitive_types4.rs solution

we get a hint by the assert_eq! part of the code as to what it's looking for, elements 2, 3, and 4 so that's what we'll do.

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

    let nice_slice = &a[1..4]; // using `a&[]` to create a slice we use the `..` to define the range

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

So in this case we define the range to to be between 1 and 4 but remember that the first element in the array is at index 0 and element 4 is index 5. Rust excludes the final element and therefore we get numbers 2, 3, 4 which correspond to index 1, 2, 3.

All we get on this one is that the test is passing and code is compiling.

🎉 🎉  The code is compiling, and the tests pass! 🎉 🎉

Just to confirm what we're talking about above, let's use a different slice range that what know is the solution, let's use &a[0..5] and see what happens.

let nice_slice = &a[0..5];

Test Failed

running 1 test
test slice_out_of_array ... FAILED

successes:

successes:

failures:

---- slice_out_of_array stdout ----
thread 'slice_out_of_array' panicked at 'assertion failed: `(left == right)`
  left: `[2, 3, 4]`,
 right: `[1, 2, 3, 4, 5]`', exercises/primitive_types/primitive_types4.rs:13:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    slice_out_of_array

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

As expected the test fails, and we see the elements that we select by using the &a[0..5] makes the test panic because the assertion failed: (left == right) in our code the left doesn't equal the right and we can see that we are accessing the elements starting at index 0 and ending at index 5.

primitive_types5.rs

// primitive_types5.rs
// Destructure the `cat` tuple so that the println will work.
// Execute `rustlings hint primitive_types5` or use the `hint` watch subcommand for a hint.

// I AM NOT DONE

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

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

primitive_types5.rs error

⚠️  Compiling of exercises/primitive_types/primitive_types5.rs failed! Please try again. Here's the output:
error: expected pattern, found `=`
 --> exercises/primitive_types/primitive_types5.rs:9:33
  |
9 |     let /* your pattern here */ = cat;
  |                                 ^ expected pattern

error: aborting due to previous error

Our instructions are to destructure the cat tuple so that the println will work, seems simple enough we have a clear indication in the code where this should take place, in our let variable assignment on line 8.

The Tuple Type

So let's quickly recap a Tuple and how we destructure it. A tuple is a compound type meaning that it can group together elements of different types, for example in our let cat = ("Furry McFurson", 3.5) we can see that we have a &str and a floating-point type. Let's keep things simple for now and not go too deep into the &str and what it means, for now we know it represents a string in our code.

So, another way we could have defined this tuple to be more explicit would have been like so (similar to our array in a previous exercise, where we defined the type for an array):

let cat: (&str, f64) = ("Furry McFurson", 3.5);

When we bind tuple to a variable, like in this case cat the variable is bound to the entire tuple as it's considered a single compound element. So to get a single element out of the tuple we have to destructure the tuple by sort of inverting the variable like this:

let (a, b) = variable

primitive_types5.rs solution

In our case we can see that in the println! statement we are looking for a name and age elements, so let's plug these into our code.

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

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

Let's check our output

Output:
====================
Furry McFurson is 3.5 years old.

====================

primitive_types6.rs

// primitive_types6.rs
// Use a tuple index to access the second element of `numbers`.
// You can put the expression for the second element where ??? is so that the test passes.
// Execute `rustlings hint primitive_types6` or use the `hint` watch subcommand for a hint.

// I AM NOT DONE

#[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!")
}

primitive_types6.rs error

⚠️  Compiling of exercises/primitive_types/primitive_types6.rs failed! Please try again. Here's the output:
error: expected expression, found `?`
  --> exercises/primitive_types/primitive_types6.rs:12:18
   |
12 |     let second = ???;
   |                  ^ expected expression

error: aborting due to previous error

Our instructions say to use a tuple index to access the second element of numbers and we can use place the expression in line 12 where the let second = ???, simple enough.

Tuple Indexing

But how do we access a tuple directly? By using the variable a period . followed by the index value.

let indexing = variable.0

Alright then, let's apply this to our problem.

primitive_types6.rs solution

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

    let second = numbers.1; // using the tuple indexing syntax to get the 2nd element which is at index `1`

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

The only tricky part is to remember that indexes start at 0 so the second element is at index 1. So if we were for example to change the number.1 to number.2, if we mistakenly thought the correct index was 2 we'd get this text in our test:

running 1 test
test indexing_tuple ... FAILED

successes:

successes:

failures:

---- indexing_tuple stdout ----
thread 'indexing_tuple' panicked at 'assertion failed: `(left == right)`
  left: `2`,
 right: `3`: This is not the 2nd number in the tuple!', exercises/primitive_types/primitive_types6.rs:14:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    indexing_tuple

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

With the correct index number we don't get anything special but a celebratory code is compiling message.

Conclusion

In this post, we tackled various exercises related to primitive types in Rust. We explored arrays, tuples, slices, and tuple indexing while understanding how to create and manipulate them. We also learned about the error messages provided by the Rust compiler and how to interpret and resolve them to make our code work as expected.

These exercises are essential for building a strong foundation in Rust, as primitive types are the building blocks for more complex data structures and programs. By mastering these concepts, you'll be well-prepared to tackle more advanced Rust topics and further enhance your Rust programming skills.

Keep practicing and exploring the Rust language, and don't forget to consult the official Rust documentation when in doubt!