Chapter 12 15 min read

Vector\<T>

Vector<T> is a growable, owning array of T. It holds a heap buffer that doubles whenever it fills up: element access is O(1) and push is amortised O(1). The vector calls libc malloc/free directly and is not tracked by Glide's auto-drop, so you must release it explicitly with free() — pair it with defer to make leaks impossible.

Import

No import needed — Vector<T> is built in.

The struct

pub struct Vector<T> {
    data: *T,        // heap buffer; null while cap == 0
    len: i32,        // number of initialised elements (<= cap)
    cap: i32,        // allocation size in elements (power of two once non-zero)
    is_arena: bool,  // true when the buffer was arena-allocated (LSP path)
}

All four fields are private; interact with the vector only through its methods.

Construction

Function Signature Description
new fn new() -> *Vector<T> Empty vector with no allocation. The first push allocates.
with_cap fn with_cap(cap: i32) -> *Vector<T> Empty vector pre-sized to hold at least cap elements without reallocating.
fn main() -> i32 {
    let v: *Vector<i32> = Vector::new();
    defer v.free();
    v.push(10);
    v.push(20);

    let w: *Vector<i32> = Vector::with_cap(64);
    defer w.free();
    for let i: i32 = 0; i < 64; i++ { w.push(i); }
    println!(w.len());   // 64
    return 0;
}

Element access & mutation

Method Signature Description
push fn push(self: *Vector<T>, x: T) Append x. Reallocates (capacity doubles) when full; amortised O(1).
pop fn pop(self: *Vector<T>) -> T Remove and return the last element. UB if empty — check is_empty() first.
get fn get(self: *Vector<T>, i: i32) -> T Read element i. No bounds check.
set fn set(self: *Vector<T>, i: i32, x: T) Overwrite element i. No bounds check.
first fn first(self: *Vector<T>) -> T First element. UB if empty.
last fn last(self: *Vector<T>) -> T Last element. UB if empty.
swap fn swap(self: *Vector<T>, i: i32, j: i32) Swap elements at i and j in place.
swap_remove fn swap_remove(self: *Vector<T>, i: i32) -> T Remove and return element i in O(1) by swapping with the last; reorders remaining elements.
reverse fn reverse(self: *Vector<T>) Reverse elements in place. O(n).
fn main() -> i32 {
    let v: *Vector<i32> = Vector::new();
    defer v.free();
    v.push(10);
    v.push(20);
    v.push(30);

    println!(v.get(0));       // 10
    v.set(1, 99);
    println!(v.get(1));       // 99
    println!(v.first());      // 10
    println!(v.last());       // 30

    let last: i32 = v.pop();  // 30
    println!(last);           // 30
    println!(v.len());        // 2
    return 0;
}

swap, swap_remove, and reverse rearrange in place:

fn main() -> i32 {
    let v: *Vector<i32> = Vector::new();
    defer v.free();
    v.push_all!(10, 20, 30, 40);

    v.swap(0, 3);                         // [40, 20, 30, 10]
    let removed: i32 = v.swap_remove(1);  // removes 20 -> [40, 10, 30]
    println!(removed);                    // 20
    v.reverse();                          // [30, 10, 40]
    println!(v.first(), v.last());        // 30 40
    return 0;
}

Size & lifetime

Method Signature Description
len fn len(self: *Vector<T>) -> i32 Number of initialised elements.
is_empty fn is_empty(self: *Vector<T>) -> bool true when len() == 0.
truncate fn truncate(self: *Vector<T>, n: i32) Shorten to at most n elements. No-op if already shorter; capacity is not reduced.
clear fn clear(self: *Vector<T>) Drop every element (len → 0). Capacity is preserved.
extend fn extend(self: *Vector<T>, other: *Vector<T>) Append every element of other; other is left intact.
free fn free(self: *Vector<T>) Release the buffer and the struct. The pointer is dangling afterwards.
fn main() -> i32 {
    let v: *Vector<i32> = Vector::new();
    defer v.free();
    v.push_all!(10, 20, 30, 40);

    v.truncate(2);            // [10, 20] — capacity unchanged
    println!(v.len());        // 2

    let other: *Vector<i32> = Vector::new();
    defer other.free();
    other.push_all!(7, 8);
    v.extend(other);          // v is now [10, 20, 7, 8]; other unchanged
    println!(v.len());        // 4
    println!(other.len());    // 2

    v.clear();
    println!(v.is_empty());   // true
    return 0;
}

The push_all! macro

macro push_all!($($x:expr),*) {
    $( self.push($x); )*
}

Pushes every passed element. Three call shapes are equivalent:

v.push_all!(1, 2, 3);
Vector::push_all!(v, 4, 5);
Vector::new().push_all!(7, 8, 9);

The vec_of! builtin

vec_of!(a, b, c) builds and returns a *Vector<T> in one expression — the element type T is inferred from the first argument. It is a compiler builtin, so it is available everywhere with no import:

let v: *Vector<i32> = vec_of!(10, 20, 30);
defer v.free();

let names = vec_of!("alice", "bob");   // inferred *Vector<string>

Because it is an expression, it works anywhere a value does — a let initializer, a function argument, or a return:

fn sum(v: *Vector<i32>) -> i32 { /* … */ }

let total = sum(vec_of!(1, 2, 3, 4, 5));   // 15

All elements must share one type; a mixed call (vec_of!(1, "two")) or an empty call (vec_of!(), which has nothing to infer T from — use Vector::new()) is rejected at compile time with a clear error.

Iteration

iter / VecIter<T>

fn iter(self: *Vector<T>) -> *VecIter<T>

pub struct VecIter<T> {
    vec: *Vector<T>,
    idx: i32,
}

impl<T> VecIter<T> {
    pub fn next(self: *VecIter<T>) -> ?T
}

iter() builds an iterator that yields each element by value; next() returns some(x) until the end, then none(). The iterator borrows the vector's buffer for the walk — mutating the vector mid-iteration is undefined behaviour.

fn main() -> i32 {
    let v: *Vector<i32> = Vector::new();
    defer v.free();
    v.push_all!(10, 20, 30);

    let it: *VecIter<i32> = v.iter();
    let mut total: i32 = 0;
    while true {
        match it.next() {
            some(x) => { total = total + x; },
            none()  => break,
        }
    }
    println!(total);   // 60
    return 0;
}

Functional combinators

These methods are generic over any Vector<T>. The transforming ones (map/filter/take/skip/take_while/skip_while) return a new *Vector that you own and must free.

Method Signature Description
map fn map<U>(self: *Vector<T>, f: fn(T) -> U) -> *Vector<U> Apply f to each element; return a new vector.
filter fn filter(self: *Vector<T>, p: fn(T) -> bool) -> *Vector<T> Keep only elements where p is true.
fold fn fold<U>(self: *Vector<T>, init: U, f: fn(U, T) -> U) -> U Reduce with accumulator; f(acc, item) returns the new accumulator.
take fn take(self: *Vector<T>, n: i32) -> *Vector<T> First n elements (clamped to length).
skip fn skip(self: *Vector<T>, n: i32) -> *Vector<T> All but the first n elements.
take_while fn take_while(self: *Vector<T>, p: fn(T) -> bool) -> *Vector<T> Elements until p returns false (then stop).
skip_while fn skip_while(self: *Vector<T>, p: fn(T) -> bool) -> *Vector<T> Drop leading elements while p is true; keep the rest from first false.
any fn any(self: *Vector<T>, p: fn(T) -> bool) -> bool true if any element matches. Short-circuits.
all fn all(self: *Vector<T>, p: fn(T) -> bool) -> bool true if all match. Empty vector → true (vacuous truth).
find fn find(self: *Vector<T>, p: fn(T) -> bool) -> ?T First matching element, or none().

map / filter / fold

fn double(x: i32) -> i32  { return x * 2; }
fn is_even(x: i32) -> bool { return x % 2 == 0; }
fn add(acc: i32, x: i32) -> i32 { return acc + x; }

fn main() -> i32 {
    let v: *Vector<i32> = Vector::new();
    defer v.free();
    v.push_all!(1, 2, 3, 4, 5);

    // Bind each stage so each owned result gets freed.
    let doubled: *Vector<i32> = v.map(double);
    defer doubled.free();                 // [2, 4, 6, 8, 10]
    let evens: *Vector<i32> = doubled.filter(is_even);
    defer evens.free();                   // [2, 4, 6, 8, 10]
    println!(evens.fold(0, add));         // 30

    println!(v.any(is_even));             // true
    println!(v.all(is_even));             // false

    match v.find(is_even) {
        some(x) => println!(x),           // 2
        none()  => println!("none"),
    }
    return 0;
}

fold is the general reducer — init seeds the accumulator and the result type U may differ from the element type. map<U> likewise changes element type (e.g. Vector<i32>Vector<string>).

take / skip / take_while / skip_while

take/skip slice by count; take_while/skip_while slice by predicate. All four return a fresh owned vector.

fn lt5(x: i32) -> bool { return x < 5; }

fn main() -> i32 {
    let v: *Vector<i32> = Vector::new();
    defer v.free();
    v.push_all!(1, 3, 7, 2, 9);

    let head: *Vector<i32> = v.take(3);
    defer head.free();                    // [1, 3, 7]

    let clamped: *Vector<i32> = v.take(99);
    defer clamped.free();
    println!(clamped.len());              // 5 (clamped to len)

    let tail: *Vector<i32> = v.skip(2);
    defer tail.free();                    // [7, 2, 9]

    let tw: *Vector<i32> = v.take_while(lt5);
    defer tw.free();                      // [1, 3]   (stops at first >= 5)

    let sw: *Vector<i32> = v.skip_while(lt5);
    defer sw.free();                      // [7, 2, 9] (keeps all from first >= 5)
    println!(tw.len(), sw.len());         // 2 3
    return 0;
}

Element-typed methods

Glide has no generic numeric/ordering trait yet, so reducers that need to add or compare elements live in concrete impl Vector<T> blocks per element type. They are only available when T is exactly the listed type.

Vector<i32>

Method Signature Description
sum pub fn sum(self: *Vector<i32>) -> i32 Sum of all elements. Empty → 0.
max pub fn max(self: *Vector<i32>) -> ?i32 Largest element, or none() if empty.
min pub fn min(self: *Vector<i32>) -> ?i32 Smallest element, or none() if empty.
fn main() -> i32 {
    let v: *Vector<i32> = Vector::new();
    defer v.free();
    v.push_all!(3, 7, 2, 9, 4);
    println!(v.sum());                          // 25
    match v.max() { some(x) => println!(x), none() => {}, }  // 9
    match v.min() { some(x) => println!(x), none() => {}, }  // 2
    return 0;
}

Vector<f64>

Method Signature Description
sum pub fn sum(self: *Vector<f64>) -> f64 Sum of all elements. Empty → 0.0.
max pub fn max(self: *Vector<f64>) -> ?f64 Largest element, or none() if empty.
min pub fn min(self: *Vector<f64>) -> ?f64 Smallest element, or none() if empty.
avg pub fn avg(self: *Vector<f64>) -> ?f64 Arithmetic mean, or none() if empty.
fn main() -> i32 {
    let v: *Vector<f64> = Vector::new();
    defer v.free();
    v.push_all!(2.0, 4.0, 6.0);
    println!(v.sum());                          // 12.0
    match v.avg() { some(x) => println!(x), none() => {}, }  // 4.0
    match v.max() { some(x) => println!(x), none() => {}, }  // 6.0
    match v.min() { some(x) => println!(x), none() => {}, }  // 2.0
    return 0;
}

Vector<string>

Method Signature Description
join pub fn join(self: *Vector<string>, sep: string) -> string Concatenate elements with sep between them. Empty → "".
concat pub fn concat(self: *Vector<string>) -> string Concatenate elements with no separator. Empty → "".
max pub fn max(self: *Vector<string>) -> ?string Lexicographically largest element, or none().
min pub fn min(self: *Vector<string>) -> ?string Lexicographically smallest element, or none().
fn main() -> i32 {
    let v: *Vector<string> = Vector::new();
    defer v.free();
    v.push_all!("apple", "banana", "cherry");
    println!(v.join(", "));   // apple, banana, cherry
    println!(v.concat());     // applebananacherry
    match v.max() { some(s) => println!(s), none() => {}, }  // cherry
    match v.min() { some(s) => println!(s), none() => {}, }  // apple
    return 0;
}

Vector<bool>

Method Signature Description
count_true pub fn count_true(self: *Vector<bool>) -> i32 Number of true elements.
count_false pub fn count_false(self: *Vector<bool>) -> i32 Number of false elements.
all_true pub fn all_true(self: *Vector<bool>) -> bool true if every element is true. Empty → true (vacuous truth).
any_true pub fn any_true(self: *Vector<bool>) -> bool true if at least one element is true. Empty → false.
fn main() -> i32 {
    let v: *Vector<bool> = Vector::new();
    defer v.free();
    v.push_all!(true, false, true, true);
    println!(v.count_true());    // 3
    println!(v.count_false());   // 1
    println!(v.all_true());      // false
    println!(v.any_true());      // true
    return 0;
}

Empty-vector conventions

Reducers that can't produce a sensible value on an empty vector return ?T; the additive/boolean ones use fixed identities. Memorise these to avoid surprises:

Call Empty result
sum (i32/f64) 0 / 0.0
max / min / avg none()
all (combinator) / all_true true (vacuous truth)
any (combinator) / any_true false
count_true / count_false 0
join / concat ""
fn main() -> i32 {
    let v: *Vector<i32> = Vector::new();
    defer v.free();
    println!(v.sum());        // 0
    match v.max() {
        some(x) => println!(x),
        none()  => println!("empty max"),   // taken
    }

    let b: *Vector<bool> = Vector::new();
    defer b.free();
    println!(b.all_true());   // true  (vacuous truth)
    println!(b.any_true());   // false
    return 0;
}

See also

  • Stringsconcat/cmp/split used by Vector<string>.
  • Preludesome/none and the ?T Option type that
  • find/max/min/avg return.