Codecademy Logo


Print Cheatsheet

What are Primitives?

Everything in Rust has a type. Some types are so commonly used in programming that they have been baked directly into the language itself. These basic types are called primitives and they do not rely upon the std library.

Boolean Primitives

The bool primitive is a boolean value, which can be either false or true.

let this_is_a_variable = true;
// A bool is its own conditional.
if this_is_a_variable {
println!("yep, its a variable");
} else {
println!("wrong universe");

Integer Primitives

Integer types that begin with an i are signed and allow negative integers, while unsigned integers begin with a u. For example, a u8 is an integer that can represent any number from 0 to 255.

Available integer primitives include:

  • Signed integers: i8, i16, i32, i64, i128, isize
  • Unsigned integers: u8, u16, u32, u64, u128, usize

usize and isize will determine the memory size based on the system architecture during compilation. When no type is specified, Rust infers the type to be i32.

let integer = 102;
let negative = -48;
// We can type annotate our integers to make sure we can handle large numbers.
let byte: u8 = 255;
let large: i64 = -9223372036854775808;
// We can also specify types as a postfix to the number.
let byte = 255u8;
let large = -9223372036854775808i64;

Floating Point Primitives

Rust has two floating point types of differing precision, f32 and f64, which are also denoted by their bit size in memory. When no type is specified, Rust infers f64. Floating point numbers are specified by their inclusion of a decimal point.

let float = 1.2;
// Floats do not require a decimal portion.
let still_a_float = 1.;
// With type annotation.
let a_less_precise_float: f32 = 2.83;

Char Primitives

A char is a character, or more specifically, a unicode scalar value. They are specified by enclosing the character within single parentheses.

Since Rust utilizes UTF-8 as an encoding scheme, some single-width characters may be composed of multiple chars.

let letter = 'q';
let accented = 'á';
let percent = '%';
let checkmark = '✓';
// This emoji is more than 1 char in length and cannot be declared as a char literal.
// We must instead use a &str.
let yourself = "😀";

Declaring and Initializing Arrays

We can declare an array with square brackets, [], containing comma-separated values. We can initialize the values of an array from an expression rather than manually defining each value. This takes the form of [expression; length] and is useful when creating large arrays.

let integers = [1, 2, 3];
// This will create an array with 20 chars of value 'e'
let many_e = ['e'; 20];
// We can use any expression to populate the default value.
let initial_value = 'E';
let more_e = [initial_value.to_ascii_lowercase(); 20];

Accessing Array Values

We can access values of collections using a special syntax referred to as index expression syntax. This allows us to directly access a single value or a range of values by index. If we try to access an index that does not exist, our code will not compile.

We can access a single value of a collection by placing the index of the desired item within square brackets after the collection, collection[3]. It is important to remember that array indexing in Rust, as with most languages, is 0-based.

let array_of_chars = ['a', 'b', 'c'];
let letter_a = array_of_chars[0]; // 'a'
let letter_c = array_of_chars[2]; // 'c'
// Rather than directly supplying a number, we can utilize any expression which evaluates to the type `usize`.
fn one() -> usize { 1 }
let letter_b = array_of_chars[one()]; // 'b'

Arrays and Ranges

We can access a range of values from a collection by supplying a beginning and ending index separated by .. within our index expression. This special syntax is referred to as range syntax. The beginning index is inclusive, and the ending index is exclusive. If we omit the beginning or ending index, the range will continue until the respective beginning or end.

let words = ["rise", "sun", "ship", "to", "sail"];
let sunship = &words[1..3]; // ["sun", "ship"]
let head = &words[..2]; // ["rise", "sun"]
let tail = &words[2..]; // ["ship", "to", "sail"]
let everything = &words[..]; // ["rise", "sun", "ship", "to", "sail"];

What is Vec?

When we need a dynamically sized collection, the std library provides us the Vec<T> type. Vec, commonly called a “Vector,” stores its data in the heap, which allows it to grow or shrink in size. We can construct Vecs using methods such as new() and from(), but Rust also provides us the vec![] macro to allow us to declare a Vec the same way we would an array.

// Initialize a new, empty Vec
let new_vec: Vec<char> = vec![];
// Initialize with values
let vec_of_chars = vec!['a', 'b', 'c'];
// This is equivalent to the previous example, but utilizing methods
let mut vec_of_chars = Vec::new();

Accessing Vec Values

Like arrays, we can use index expressions to access the values of a Vec. Unlike an array, if we try to access an index that does not exist, our code will successfully compile but will panic at runtime. To avoid this, accessing the values on a Vec is generally accomplished through methods such as get() and first(). These methods return an Option<T> which allows us to handle situations where the index does not exist.

let vec_of_chars = vec!['a', 'b', 'c'];
let a = vec_of_chars.first(); // Some('a')
// Note that the `get()` method does not use 0-based indexing
let c = vec_of_chars.get(2); // Some('c')
let f = vec_of_chars.get(9); // None

What are Strings?

A String is stored on the heap which allows us to mutate the value at will. While heap memory is never as fast as the stack, the heap allows Rust to automatically resize the allocated memory when needed during runtime.

let empty_string = String::new();
let value_string = String::from("Cyan");
let mut mutable_string = String::from("Indigo");
mutable_string.push_str(" and Maroon.");

What are &strs?

&strs are immutable slices of bytes of a fixed size and are often referred to as “string slices.” Since &strs are only a reference to data, our type declarations will always have the preceding &. Their data is stored on the stack which makes them computationally efficient.

let immutable = "I am a location in memory";
// A type annotated str.
let value: &str = ", and cannot be mutated.";
// A type annotated str with an explicit lifetime.
let baked: &'static str = "I am baked into the binary!";
println!("{immutable} {value}");


Strings are very ergonomic in terms of how we can compose them. Here are some examples of concatenation in Rust.

let a = "Welcome";
let b = " to";
let c = " the";
let d = " show!";
let welcome1 = format!("{a}{b}{c}{d}!");
let welcome2 = [a,b,c,d,"!"].concat();
let welcome3 = a.to_string() + b + c + d + "!";
// We can even append to a mutable String with the `+=` operator.
let mut welcome4 = welcome3;
welcome4 += " Lets have some fun!";

String Conversions

We can turn a &str into a String using the String::from() or to_string() methods. Both are commonly utilized in Rust and using one over the other is a matter of personal preference.

To utilize a String as a &str, we only have to reference the value.

// Converting from &str to String
let pineapple = "pineapple";
let new_string = String::from(pineapple);
let new_string = pineapple.to_string();
// Converting from String to &str
fn say_fruit(name: &str) {
let papaya = String::from("Papaya");