100-exercises-to-learn-rust/book/src/06_ticket_management/01_arrays.md

81 lines
2.4 KiB
Markdown
Raw Normal View History

2024-05-13 04:21:03 +08:00
# Arrays
As soon as we start talking about "ticket management" we need to think about a way to store _multiple_ tickets.
In turn, this means we need to think about collections. In particular, homogeneous collections:
we want to store multiple instances of the same type.
What does Rust have to offer in this regard?
## Arrays
A first attempt could be to use an **array**.
Arrays in Rust are fixed-size collections of elements of the same type.
Here's how you can define an array:
```rust
// Array type syntax: [ <type> ; <number of elements> ]
let numbers: [u32; 3] = [1, 2, 3];
```
This creates an array of 3 integers, initialized with the values `1`, `2`, and `3`.
The type of the array is `[u32; 3]`, which reads as "an array of `u32`s with a length of 3".
### Accessing elements
You can access elements of an array using square brackets:
```rust
let first = numbers[0];
let second = numbers[1];
let third = numbers[2];
```
The index must be of type `usize`.
Arrays are **zero-indexed**, like everything in Rust. You've seen this before with string slices and field indexing in
tuples/tuple-like variants.
### Out-of-bounds access
If you try to access an element that's out of bounds, Rust will panic:
```rust
let numbers: [u32; 3] = [1, 2, 3];
let fourth = numbers[3]; // This will panic
```
This is enforced at runtime using **bounds checking**. It comes with a small performance overhead, but it's how
Rust prevents buffer overflows.
In some scenarios the Rust compiler can optimize away bounds checks, especially if iterators are involved—we'll speak
more about this later on.
If you don't want to panic, you can use the `get` method, which returns an `Option<&T>`:
```rust
let numbers: [u32; 3] = [1, 2, 3];
assert_eq!(numbers.get(0), Some(&1));
// You get a `None` if you try to access an out-of-bounds index
// rather than a panic.
assert_eq!(numbers.get(3), None);
```
### Performance
Since the size of an array is known at compile-time, the compiler can allocate the array on the stack.
If you run the following code:
```rust
let numbers: [u32; 3] = [1, 2, 3];
```
You'll get the following memory layout:
```text
+---+---+---+
Stack: | 1 | 2 | 3 |
+---+---+---+
```
In other words, the size of an array is `std::mem::size_of::<T>() * N`, where `T` is the type of the elements and `N` is
the number of elements.
You can access and replace each element in `O(1)` time.