DEV Community

Cover image for [Rust Guide] 3.3. Data Types - Compound Types
SomeB1oody
SomeB1oody

Posted on

[Rust Guide] 3.3. Data Types - Compound Types

3.3.0. Before We Begin

Welcome to Chapter 3 of this Rust self-study series. It has 6 sections:

  • Variables and Mutability
  • Data Types: Scalar Types
  • Data Types: Compound Types (this article)
  • Functions and Comments
  • Control Flow: if else
  • Control Flow: Loops

Through the guessing game in Chapter 2 (beginners who have not read it are strongly encouraged to take a look), you should now have learned the basic Rust syntax. In Chapter 3, we will go one level deeper and learn the general programming concepts in Rust.

3.3.1. An Introduction to Compound Types

  • Compound types can group multiple values into a single type.
  • Rust provides two basic compound types: tuples and arrays.

3.3.1. Tuple

Tuple characteristics:

  • A tuple can group multiple values of different types into a single type.
  • Tuples have a fixed length: once declared, they cannot change.

Creating a tuple:

  • Place the values inside parentheses, separated by commas.
  • Each position in the tuple corresponds to a type, and the types of the tuple's elements do not have to be the same.
fn main(){
    let tup: (u32, f32, i64) = (6657, 0.0721, 114514);
    println!("{},{},{}", tup.0, tup.1, tup.2);
    // Output: 6657,0.0721,114514
}
Enter fullscreen mode Exit fullscreen mode

Getting tuple element values:

  • You can use pattern matching to destructure a tuple and obtain its element values.
fn main(){
    let tup: (u32, f32, i64) = (6657, 0.0721, 114514);
    let (x, y, z) = tup;
    println!("{},{},{}", x, y, z);
    // Output: 6657,0.0721,114514
}
Enter fullscreen mode Exit fullscreen mode

Accessing tuple elements:

  • Use dot notation after the tuple variable, followed by the element index.
println!("{},{},{}", tup.0, tup.1, tup.2);
Enter fullscreen mode Exit fullscreen mode

3.3.2. Arrays

Array characteristics:

  • Every element in an array must have the same type.
  • Arrays can also store multiple values in a single type.
  • Arrays have a fixed length.

Declaring an array:

  • Put the values inside square brackets, separated by commas.
let a = [1, 1, 4, 5, 1, 4];
Enter fullscreen mode Exit fullscreen mode

Uses for arrays:

  • If you want your data on the stack instead of the heap, or you want to guarantee a fixed number of elements, arrays are a better choice.
  • Arrays are less flexible than vectors (which we will discuss later).
    • Vectors are provided by the standard library, while arrays are built into the language and available through the prelude module, which is also part of the standard library.
    • A vector's length can change.
    • If you are unsure whether to use an array or a vector, you probably should use a vector.

Array type syntax:

  • The type of an array is written as [type; length].
let machine: [u32; 4] = [6, 6, 5, 7];
Enter fullscreen mode Exit fullscreen mode

Another way to declare an array:

  • If every element in the array has the same value, you can:
    • Specify the initial value inside square brackets
    • Follow it with a ;
    • Then add the array length
let a = [3; 2];
let b = [3, 3, 3];
Enter fullscreen mode Exit fullscreen mode

In this example, a and b are equivalent.

Accessing array elements:

  • Arrays are a single contiguous block of memory allocated on the stack.
  • You can use an index to access an array element.
let machine = [6, 6, 5, 7];
let wjq = machine[0];
Enter fullscreen mode Exit fullscreen mode
  • If the index is out of bounds:
    • Rust may detect it at compile time in cases where the compiler can prove the error
    • Otherwise, it will panic at runtime, because Rust does not allow the program to keep reading memory at that address

An array is backed by a contiguous block of memory. Suppose the first element of an array is at memory position x; then the second element is located at x + the size of the first element, and so on.

If the index is larger than the actual length of the array, the program will read memory outside the array, and that memory may contain anything. In C, there is no bounds checking at all. In C++, ordinary arrays do not have it either; only std::array does. In Rust, bounds checking is enforced.

Feature C C++ Rust
Memory model Contiguous Contiguous Contiguous
Safety No bounds checking std::array has bounds checking; ordinary arrays do not Bounds checking is enforced
Dynamic arrays Manual memory management required std::vector Vec
Multidimensional arrays Yes Yes Yes
Special abilities Simple and efficient Rich STL containers Ownership and borrow checking

But Rust only performs simple bounds checks on arrays. If the code becomes slightly more complex, the compiler may not be able to check it at compile time, so the check has to happen at runtime.

let a = 5;
let machine = [6, 6, 5, 7];
let wjq = machine[a];
Enter fullscreen mode Exit fullscreen mode

This code will compile, but it will panic at runtime if a is out of bounds.

let a = [1, 9, 10, 4, 5];
let machine = [6, 6, 5, 7];
let wjq = machine[a[4]];
Enter fullscreen mode Exit fullscreen mode

Depending on how much the compiler can determine ahead of time, this code may also fail early, but if it is not caught at compile time, it will panic at runtime.

Top comments (0)