Iterators
stdlib::iter provides the Iterator trait, plugs Vector's VecIter into it, and ships a family of free-function combinators that transform, reduce, and construct Vector<T> values. The transformers are eager: each one walks the input once and returns a fresh, fully-materialized *Vector<U> rather than a lazy adapter.
Import
import stdlib::iter::*;
Public surface at a glance
| Kind | Item |
|---|---|
| trait | Iterator (assoc. type Item, method next) |
| impl | Iterator for VecIter<T> |
| transformers | iter_map, iter_filter, iter_take, iter_skip, iter_take_while, iter_skip_while |
| reducers | iter_fold, iter_count, iter_any, iter_all, iter_find |
| numeric reducers | iter_sum_int, iter_max_int, iter_min_int |
| constructors | iter_range, iter_range_step, iter_repeat |
VecIter<T> itself is declared in the builtins (builtins::vector) along with Vector::iter; this module only adds the Iterator impl for it.
The Iterator trait
pub trait Iterator {
type Item;
fn next(self: *Self) -> ?Self::Item;
}
A type is an iterator if it can hand back the next element wrapped in an option — some(x) while elements remain, none() once exhausted. The trait has one associated type, Item, naming the element type.
The module supplies one implementation, for VecIter<T> (the cursor returned by Vector::iter):
impl<T> Iterator for VecIter<T> {
type Item = T;
fn next(self: *VecIter<T>) -> ?T {
if self.idx >= self.vec.len { return none(); }
let v: T = self.vec.data[self.idx];
self.idx = self.idx + 1;
return some(v);
}
}
Because the impl lives in this module, importing stdlib::iter::* is what makes v.iter() usable through a trait bound such as fn f<I: Iterator>(it: *I).
VecIter<T>
pub struct VecIter<T> {
vec: *Vector<T>,
idx: i32,
}
| Field | Type | Meaning |
|---|---|---|
vec |
*Vector<T> |
borrowed vector being walked |
idx |
i32 |
next index to yield |
You obtain a *VecIter<T> from v.iter() and pull elements with .next() until it returns none():
import stdlib::iter::*;
fn main() -> i32 {
let v: *Vector<i32> = Vector::new();
defer v.free();
v.push_all!(10, 20, 30);
let it: *VecIter<i32> = v.iter();
while true {
match it.next() {
some(x) => println!(x),
none() => break,
}
}
return 0;
}
A generic function can accept any iterator via a trait bound:
import stdlib::iter::*;
fn count_all<I: Iterator>(it: *I) -> i32 {
let mut n: i32 = 0;
while true {
match it.next() {
some(_) => { n = n + 1; },
none() => break,
}
}
return n;
}
fn main() -> i32 {
let v: *Vector<i32> = iter_range(1, 5); // [1,2,3,4]
defer v.free();
println!(count_all(v.iter())); // 4
return 0;
}
Transformers — return a new *Vector<U>
Each transformer allocates and returns a brand-new vector that the caller owns. Bind the result with let v* = ... for scope-end auto-drop, or with an explicit raw let v: *Vector<T> = ... when you intend to pass it onward to another helper (see the mistake callout below).
| Function | Signature | Result |
|---|---|---|
iter_map |
fn iter_map<T, U>(v: *Vector<T>, f: fn(T) -> U) -> *Vector<U> |
apply f to each element |
iter_filter |
fn iter_filter<T>(v: *Vector<T>, p: fn(T) -> bool) -> *Vector<T> |
keep elements where p is true |
iter_take |
fn iter_take<T>(v: *Vector<T>, n: i32) -> *Vector<T> |
first n (clamped to length, n<0→empty) |
iter_skip |
fn iter_skip<T>(v: *Vector<T>, n: i32) -> *Vector<T> |
drop first n (n<0 treated as 0) |
iter_take_while |
fn iter_take_while<T>(v: *Vector<T>, p: fn(T) -> bool) -> *Vector<T> |
prefix until p first returns false |
iter_skip_while |
fn iter_skip_while<T>(v: *Vector<T>, p: fn(T) -> bool) -> *Vector<T> |
suffix from the first p-false element |
iter_map, iter_filter
import stdlib::iter::*;
fn double(x: i32) -> i32 { return x * 2; }
fn keep_pos(x: i32) -> bool { return x > 0; }
fn main() -> i32 {
let v: *Vector<i32> = iter_range(0 - 3, 5); // [-3,-2,-1,0,1,2,3,4]
let m: *Vector<i32> = iter_map(v, double);
let f: *Vector<i32> = iter_filter(m, keep_pos); // [2,4,6,8]
let total: i32 = iter_sum_int(f); // 20
println!(total);
return 0;
}
iter_map can change the element type (T → U); iter_filter always preserves it. Neither mutates the input — the source vector is untouched.
iter_take, iter_skip, iter_take_while, iter_skip_while
import stdlib::iter::*;
fn lt3(x: i32) -> bool { return x < 3; }
fn main() -> i32 {
let v: *Vector<i32> = iter_range(0, 6); // [0,1,2,3,4,5]
let a* = iter_take(v, 3); // [0,1,2]
let b* = iter_skip(v, 2); // [2,3,4,5]
let c* = iter_take_while(v, lt3); // [0,1,2]
let d* = iter_skip_while(v, lt3); // [3,4,5]
println!(a.len(), b.len(), c.len(), d.len());
return 0;
}
take_while stops at the first element failing p; skip_while discards the leading run of p-true elements and keeps everything from the first p-false element onward (including later elements that happen to satisfy p).
Boundary behavior
| Call | Input [0,1,2,3] |
Result |
|---|---|---|
iter_take(v, 99) |
oversized n |
clamped → [0,1,2,3] |
iter_take(v, 0 - 5) |
negative n |
[] |
iter_skip(v, 0 - 5) |
negative n |
treated as 0 → [0,1,2,3] |
iter_take_while(v, p) |
p true for none |
[] |
iter_skip_while(v, p) |
p true for all |
[] |
import stdlib::iter::*;
fn main() -> i32 {
let v: *Vector<i32> = iter_range(0, 4); // [0,1,2,3]
let big* = iter_take(v, 99); // [0,1,2,3]
let neg* = iter_take(v, 0 - 5); // []
let sk* = iter_skip(v, 0 - 5); // [0,1,2,3]
println!(big.len(), neg.len(), sk.len()); // 4 0 4
v.free();
return 0;
}
Chaining transformers
Transformers consume a *Vector and return a fresh one, so they chain by feeding each result into the next. When a result is consumed by another helper, bind it as a plain raw pointer (let x: *Vector<i32> = ...), then free each intermediate yourself:
import stdlib::iter::*;
fn double(x: i32) -> i32 { return x * 2; }
fn is_even(x: i32) -> bool { return x % 2 == 0; }
fn main() -> i32 {
let v: *Vector<i32> = iter_range(1, 7); // [1,2,3,4,5,6]
let evens: *Vector<i32> = iter_filter(v, is_even); // [2,4,6]
let dbl: *Vector<i32> = iter_map(evens, double); // [4,8,12]
let total: i32 = iter_sum_int(dbl); // 24
println!(total);
v.free();
evens.free();
dbl.free();
return 0;
}
Reducers — return a value
Reducers read the vector and collapse it to a single result. iter_find returns an option; the rest return plain values. None of them free or mutate the input.
| Function | Signature | Result |
|---|---|---|
iter_fold |
fn iter_fold<T, U>(v: *Vector<T>, init: U, f: fn(U, T) -> U) -> U |
left fold; f(acc, item) → new acc |
iter_count |
fn iter_count<T>(v: *Vector<T>) -> i32 |
element count (same as v.len()) |
iter_any |
fn iter_any<T>(v: *Vector<T>, p: fn(T) -> bool) -> bool |
true if any matches (short-circuits) |
iter_all |
fn iter_all<T>(v: *Vector<T>, p: fn(T) -> bool) -> bool |
true if all match; empty → true |
iter_find |
fn iter_find<T>(v: *Vector<T>, p: fn(T) -> bool) -> ?T |
first match as some(x), else none() |
import stdlib::iter::*;
fn add(acc: i32, x: i32) -> i32 { return acc + x; }
fn is_even(x: i32) -> bool { return x % 2 == 0; }
fn is_neg(x: i32) -> bool { return x < 0; }
fn is_pos(x: i32) -> bool { return x > 0; }
fn main() -> i32 {
let v: *Vector<i32> = iter_range(1, 5); // [1,2,3,4]
let sum: i32 = iter_fold(v, 0, add); // 10
let n: i32 = iter_count(v); // 4
let anyNeg: bool = iter_any(v, is_neg); // false
let allPos: bool = iter_all(v, is_pos); // true
println!(sum, n, anyNeg, allPos);
match iter_find(v, is_even) {
some(x) => println!(x), // 2
none() => {},
}
return 0;
}
iter_fold — the general reducer
fold is the most general reducer: the accumulator type U is independent of the element type T, so it can build a value of any shape. Here it folds a Vector<i32> into a comma-separated string:
import stdlib::iter::*;
fn join_csv(acc: string, x: i32) -> string {
if acc.len() == 0 { return format!("{}", x); }
return acc.concat(",").concat(format!("{}", x));
}
fn main() -> i32 {
let v: *Vector<i32> = iter_range(1, 5); // [1,2,3,4]
defer v.free();
let s: string = iter_fold(v, "", join_csv);
println!(s); // 1,2,3,4
return 0;
}
iter_any/iter_all short-circuit (stop at the first hit / first miss); iter_find returns the first matching element wrapped in ?T. iter_fold always visits every element.
Type-specific numeric reducers
Glide's generics do not constrain primitives to an Ord/numeric trait, so summing and finding extremes are provided as concrete i32 functions.
| Function | Signature | Result |
|---|---|---|
iter_sum_int |
fn iter_sum_int(v: *Vector<i32>) -> i32 |
sum of elements (0 for empty) |
iter_max_int |
fn iter_max_int(v: *Vector<i32>) -> ?i32 |
largest element, none() if empty |
iter_min_int |
fn iter_min_int(v: *Vector<i32>) -> ?i32 |
smallest element, none() if empty |
import stdlib::iter::*;
fn main() -> i32 {
let v: *Vector<i32> = Vector::new();
defer v.free();
v.push_all!(3, 7, 2, 9, 4);
match iter_max_int(v) {
some(m) => println!("max", m), // 9
none() => {},
}
match iter_min_int(v) {
some(m) => println!("min", m), // 2
none() => {},
}
println!("sum", iter_sum_int(v)); // 25
return 0;
}
iter_max_int / iter_min_int return ?i32 precisely so the empty case has a representable answer; unwrap with match, .has/.val, or ??.
Constructors
| Function | Signature | Result |
|---|---|---|
iter_range |
fn iter_range(start: i32, end: i32) -> *Vector<i32> |
[start, end); empty if end <= start |
iter_range_step |
fn iter_range_step(start: i32, end: i32, step: i32) -> *Vector<i32> |
start, start+step, … < end; empty if step <= 0 |
iter_repeat |
fn iter_repeat<T>(value: T, n: i32) -> *Vector<T> |
n copies of value; empty if n <= 0 |
import stdlib::iter::*;
fn main() -> i32 {
let r* = iter_range(0, 5); // [0,1,2,3,4]
let s* = iter_range_step(0, 10, 2); // [0,2,4,6,8]
let z* = iter_repeat(0, 4); // [0,0,0,0]
let t* = iter_repeat("x", 3); // ["x","x","x"]
println!(r.len(), s.len(), z.len(), t.len());
return 0;
}
iter_range is the workhorse for building test data and is the canonical input to the transformers and reducers above. iter_range_step is half-open like iter_range and guards against non-positive steps (which would otherwise loop forever) by returning an empty vector.
Constructor edge cases
import stdlib::iter::*;
fn main() -> i32 {
let a* = iter_range_step(0, 10, 3); // [0,3,6,9]
let b* = iter_range_step(10, 0, 1); // [] (end <= start)
let c* = iter_range_step(0, 5, 0); // [] (step <= 0)
let d* = iter_range_step(0, 5, 0 - 2); // [] (step < 0)
println!(a.len(), b.len(), c.len(), d.len()); // 4 0 0 0
let r* = iter_repeat(7, 3); // [7,7,7]
let z* = iter_repeat(7, 0); // []
let n* = iter_repeat(7, 0 - 4); // [] (n <= 0)
println!(r.len(), z.len(), n.len()); // 3 0 0
return 0;
}
Method-form cross-reference
Every transformer/reducer that takes a single *Vector<T> as its first argument has an equivalent method on Vector<T> (defined in builtins::vector). The free function and the method do the same thing — pick whichever reads better.
| Free function | Vector method | Notes |
|---|---|---|
iter_map(v, f) |
v.map(f) |
element type may change |
iter_filter(v, p) |
v.filter(p) |
|
iter_take(v, n) |
v.take(n) |
|
iter_skip(v, n) |
v.skip(n) |
|
iter_take_while(v, p) |
v.take_while(p) |
|
iter_skip_while(v, p) |
v.skip_while(p) |
|
iter_fold(v, init, f) |
v.fold(init, f) |
|
iter_count(v) |
v.len() |
iter_count is just v.len() |
iter_any(v, p) |
v.any(p) |
|
iter_all(v, p) |
v.all(p) |
|
iter_find(v, p) |
v.find(p) |
returns ?T |
iter_sum_int(v) |
v.sum() |
method form on Vector<i32>/Vector<f64> |
iter_max_int(v) |
v.max() |
?T |
iter_min_int(v) |
v.min() |
?T |
iter_range/iter_range_step/iter_repeat have no method form — they are constructors, not operations on an existing vector.
import stdlib::iter::*;
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 is_pos(x: i32) -> bool { return x > 0; }
fn is_neg(x: i32) -> bool { return x < 0; }
fn main() -> i32 {
let v: *Vector<i32> = Vector::new();
defer v.free();
v.push_all!(1, 2, 3, 4, 5);
let m* = v.map(double); // [2,4,6,8,10]
let f* = v.filter(is_even); // [2,4]
let h* = v.take(3); // [1,2,3]
let t* = v.skip(2); // [3,4,5]
println!(m.len(), f.len(), h.len(), t.len());
println!(v.fold(0, add)); // 15
println!(v.any(is_neg)); // false
println!(v.all(is_pos)); // true
println!(v.sum()); // 15
match v.max() {
some(x) => println!("max", x), // 5
none() => {},
}
match v.find(is_even) {
some(x) => println!("first even", x), // 2
none() => {},
}
return 0;
}
Ownership recap
- Transformers and constructors allocate a new
*Vectorthe caller owns. - Reducers borrow their input and free nothing; the input vector remains valid.
Vector::new()produces a raw*Vector; pair it withdefer v.free()(andv.iter()allocates a*VecIterthat borrowsv; keepvalive while
Bind with let v* = ... to auto-free at scope end, or with a raw let v: *Vector<T> = ... when you will hand it to another helper.
use push_all!, which works on raw vectors) when you manage it manually.
iterating and don't resize it mid-walk.