An Overview of Rust’s Built-In Data Types

6 min read Original article ↗

4

Goodness is a highly experienced technical writer and backend engineer who specializes in building scalable systems using Go and Rust. With 4 years of industry experience and 3 years of technical writing experience, Goodness has honed his skills in these languages and enjoys sharing his knowledge with the community.

Aside from his contributions to MUO, Goodness has also written over 200 technical articles for reputable companies such as DigitalOcean, LogRocket, MatterMost, and FreeCodeCamp. His writing focuses on topics related to Go, Rust, Web3, data, and backend engineering. Goodness's articles are highly regarded in the industry for their quality, depth, and relevance.

Goodness's expertise in these areas is supported by his certifications in Go and Rust, as well as his attendance at industry conferences and his efforts to stay up-to-date on the latest trends and technologies. He is a passionate and dedicated professional who is always striving to improve his skills and knowledge.

Rust is a statically-typed modern programming language designed for performance, reliability, and security. As in other statically-typed languages, you declare Rust data types at compile time. This makes it easier to catch type errors before you run your code.

Rust offers scalar, compound, reference types, structs, enums, and strings. Its type inference provides functionality for writing concise code while maintaining the safety of a statically-typed language.

Integers in Rust

Rust provides signed and unsigned integer types classified based on the number of bits. The signed integer types are i8, i16, i32, and i64 representing 8-bit, 16-bit, 32-bit, and 64-bit signed integers, respectively. It also supports unsigned integer types are u8, u16, u32, and u64, representing 8-bit, 16-bit, 32-bit, and 64-bit unsigned integers.

// signed integers
let a: i8 = -10;
let b: i16 = -2048;
let c: i32 = -2147483648;
let d: i64 = -9223372036854775808;

// unsigned integers
let e: u8 = 255;
let f: u16 = 65535;
let g: u32 = 4294967295;
let h: u64 = 18446744073709551615;

Rust uses the i32 type for integer literals by default.

Rust Floating Point Types

Rust provides f32 and f64 as floating point types that represent single-precision and double-precision floating point numbers. The f32 type uses 32 bits to store values, and the f64 type uses 64 bits.

Floating-point numbers in Rust follow the IEEE 754 standard for floating-point arithmetic.

let a = 3.14159265358979323_f32;
let b = 2.718281828459045235_f64;

Using Rust Booleans

Rust provides a bool type to represent true or false values. Booleans are often used in conditional and control flow statements for program decision-making.

let variable_1: bool = true;
let variable_2: bool = false;

You can compare boolean values with the equality operator, ==, and the inequality operator, !=. Rust does not define the comparison operators, <, >, <=, and >=, for bool values.

let variable_1: bool = true;
let variable_2: bool = false;

if variable_1 == variable_2 {
    println!("variable_1 is equal to variable_2");
} else if variable_1 != variable_2 {
    println!("variable_1 is not equal to variable_2");
}

The Char Type

The Rust char type represents a single Unicode scalar value that can represent any character in the Unicode standard. You can specify a char value using single quotes.

// Declaring a char value
let c = 'a';

The char type is helpful for working with emojis in Rust.

Tuples in Rust

The tuple data structure lets you group more than one value into a single compound value. These values can have the same type or different types. You can declare tuples by writing them as a comma-separated list of values surrounded by parentheses.

Here’s how you can declare a tuple with 32-bit integers, strings, and float64 values.

let tup: (i32, &str, f64) = (500, "Hello", 3.14);

Tuples have a fixed length, and you can use them to return multiple values from a function or pass multiple values to functions as a single argument.

You can access individual elements of a tuple by destructuring it using pattern matching or directly accessing individual elements using dot (.) syntax and an index.

Here’s how you can access individual elements of a struct using pattern matching:

let my_tuple = (10, "Hello, World!", false);

let (x, y, z) = my_tuple;

println!("The first element is: {}", x);
println!("The second element is: {}", y);
println!("The third element is: {}", z);

Here’s how you can access individual elements using dot notation:

let my_tuple = (10, "Hello, World!", false);

println!("The first element is: {}", my_tuple.0);
println!("The second element is: {}", my_tuple.1);
println!("The third element is: {}", my_tuple.2);

Tuples are very useful when grouping related data into a single value. They can also improve the readability of your code if you use them sparingly.

Arrays in Rust

An array is a collection of elements of the same type with a fixed length. You write Rust arrays as a square-bracketed list of values, separated by commas.

Here’s how you can declare arrays in Rust:

let arr = [1, 2, 3, 4, 5];

You can't change the number of elements in an array once you have declared it, but you can access, modify, and manipulate individual elements of an array using indexing.

let mut my_array = [1, 2, 3, 4, 5];

// Accessing elements
println!("The first element is: {}", my_array[0]);

// Modifying elements
my_array[0] = 100;
println!("The first element after modification is: {}", my_array[0]);

// Looping over an array and manipulating elements
for i in 0..my_array.len() {
    my_array[i] *= 2;
}

// printing the array
println!("The array after manipulation: {:?}", my_array);

Rust Arrays are stored on the stack and have a contiguous memory allocation, so accessing elements of an array is fast and efficient. This makes arrays suitable for situations where you need to store and process many elements.

result from the Rust arrays operation
image credit: Ukeje Goodness Screenshot

Working With Rust Slices

A slice is a data structure that allows referencing a contiguous sequence of elements in a collection. Slices are represented by the &[T] type, where T is the type of elements stored in the slice.

fn main() {
    // declare an array
    let my_array = [1, 2, 3, 4, 5];

    // create a slice from the array
    let my_slice = &my_array[1..3];

    // print the slice
    println!("Slice: {:?}", my_slice);
}

Notice how the range syntax, .., extracts a slice from an array using the starting index and an index one greater than the end:

result from the Rust slices operation
image credit: Ukeje Goodness Screenshot

Slices are dynamic, so Rust can determine their length at runtime. You can also pass slices as arguments to functions without needing heap allocation.

You’ll commonly use slices for string operations and to pass subsets of data to functions. They are a powerful and efficient tool for managing collections in Rust, providing a more flexible alternative to arrays.

You Can Build WebAssembly-Powered Frontend Web Apps in Rust

Knowledge of data types is pivotal to your Rust journey since you'll use them for most operations while building applications.

WebAssembly is a low-level binary format that runs on modern web browsers, with near-native performance. It lets you write code in many different languages and transpiling it to WebAssembly.

WebAssembly is gaining adoption through Rust. There are many frameworks like Yew, Sycamore, and Seed that you can use to build WebAssembly-powered frontends with Rust.