Arrays
Rue has fixed-size arrays with bounds checking at runtime.
Creating Arrays
const std = @import("std");
fn main() -> i32 {
let numbers = [10, 20, 30, 40, 50];
// Access by index
println(@to_string(numbers[0])); // prints: 10
println(@to_string(numbers[4])); // prints: 50
numbers[0]
}
Array indices are zero-based and must have an integer type. Loop examples often use u64 because lengths are represented as u64.
Array Types
The type of an array includes its element type and length:
const std = @import("std");
fn main() -> i32 {
let a: [i32; 3] = [1, 2, 3]; // 3 elements
let b: [bool; 2] = [true, false]; // 2 booleans
println(@to_string(a[0])); // prints: 1
0
}
Iterating Over Arrays
Use a while loop with an index:
const std = @import("std");
fn main() -> i32 {
let numbers = [10, 20, 30, 40, 50];
let mut sum = 0;
let mut i: u64 = 0;
while i < 5 {
sum = sum + numbers[i];
i = i + 1;
}
println(@to_string(sum)); // prints: 150
sum
}
Mutable Arrays
Arrays are mutable if declared with let mut:
const std = @import("std");
fn main() -> i32 {
let mut scores = [0, 0, 0];
scores[0] = 100;
scores[1] = 85;
scores[2] = 92;
println(@to_string(scores[0] + scores[1] + scores[2])); // prints: 277
0
}
Bounds Checking
Rue checks array bounds. A constant out-of-bounds index is rejected at compile time:
const std = @import("std");
fn main() -> i32 {
let arr = [1, 2, 3];
println(@to_string(arr[10])); // Error: index out of bounds
0
}
Dynamic out-of-bounds indexes are checked at runtime. Together, these checks prevent memory safety bugs common in C and C++.
Example: Finding Maximum
const std = @import("std");
fn main() -> i32 {
let numbers = [64, 34, 25, 12, 22];
let mut max = numbers[0];
let mut i: u64 = 1;
while i < 5 {
if numbers[i] > max {
max = numbers[i];
}
i = i + 1;
}
println(@to_string(max)); // prints: 64
max
}
Fixed Arrays vs Growable Buffers
Fixed arrays are best when the length is known at compile time. Their length is part of the type: [i32; 3] and [i32; 5] are different types.
When you need a collection that grows at runtime, use the standard library's ArrayBuf(T). Import the standard library explicitly and access the buffer through the std.arraybuf namespace:
const std = @import("std");
fn main() -> i32 {
let Buffer = std.arraybuf.ArrayBuf(i32);
let MaybeI32 = std.option.Option(i32);
let mut values = Buffer.new();
values.push(10);
values.push(20);
values.push(30);
println("len = " + @to_string(values.len()));
let second = match values.get(1) {
MaybeI32.Some(n) => n,
MaybeI32.None => -1,
};
println("second = " + @to_string(second));
second
}
ArrayBuf(i32) owns heap storage and frees it automatically when the buffer is dropped. Its get method returns Option(i32) rather than trapping:
Option.Some(value)means the index was in bounds.Option.Nonemeans there was no element at that index.
Use fixed arrays for small, known-size data and ArrayBuf(T) when the program discovers the number of elements as it runs. The slice and growable string parts of Rue's collection/string design are still in progress, so this tutorial keeps to the implemented fixed-array and ArrayBuf path.