|
| 1 | +% Containers and iterators |
| 2 | + |
| 3 | +# Containers |
| 4 | + |
| 5 | +The container traits are defined in the `std::container` module. |
| 6 | + |
| 7 | +## Unique and managed vectors |
| 8 | + |
| 9 | +Vectors have `O(1)` indexing and removal from the end, along with `O(1)` |
| 10 | +amortized insertion. Vectors are the most common container in Rust, and are |
| 11 | +flexible enough to fit many use cases. |
| 12 | + |
| 13 | +Vectors can also be sorted and used as efficient lookup tables with the |
| 14 | +`std::vec::bsearch` function, if all the elements are inserted at one time and |
| 15 | +deletions are unnecessary. |
| 16 | + |
| 17 | +## Maps and sets |
| 18 | + |
| 19 | +Maps are collections of unique keys with corresponding values, and sets are |
| 20 | +just unique keys without a corresponding value. The `Map` and `Set` traits in |
| 21 | +`std::container` define the basic interface. |
| 22 | + |
| 23 | +The standard library provides three owned map/set types: |
| 24 | + |
| 25 | +* `std::hashmap::HashMap` and `std::hashmap::HashSet`, requiring the keys to |
| 26 | + implement `Eq` and `Hash` |
| 27 | +* `std::trie::TrieMap` and `std::trie::TrieSet`, requiring the keys to be `uint` |
| 28 | +* `extra::treemap::TreeMap` and `extra::treemap::TreeSet`, requiring the keys |
| 29 | + to implement `TotalOrd` |
| 30 | + |
| 31 | +These maps do not use managed pointers so they can be sent between tasks as |
| 32 | +long as the key and value types are sendable. Neither the key or value type has |
| 33 | +to be copyable. |
| 34 | + |
| 35 | +The `TrieMap` and `TreeMap` maps are ordered, while `HashMap` uses an arbitrary |
| 36 | +order. |
| 37 | + |
| 38 | +Each `HashMap` instance has a random 128-bit key to use with a keyed hash, |
| 39 | +making the order of a set of keys in a given hash table randomized. Rust |
| 40 | +provides a [SipHash](https://131002.net/siphash/) implementation for any type |
| 41 | +implementing the `IterBytes` trait. |
| 42 | + |
| 43 | +## Double-ended queues |
| 44 | + |
| 45 | +The `extra::deque` module implements a double-ended queue with `O(1)` amortized |
| 46 | +inserts and removals from both ends of the container. It also has `O(1)` |
| 47 | +indexing like a vector. The contained elements are not required to be copyable, |
| 48 | +and the queue will be sendable if the contained type is sendable. |
| 49 | + |
| 50 | +## Priority queues |
| 51 | + |
| 52 | +The `extra::priority_queue` module implements a queue ordered by a key. The |
| 53 | +contained elements are not required to be copyable, and the queue will be |
| 54 | +sendable if the contained type is sendable. |
| 55 | + |
| 56 | +Insertions have `O(log n)` time complexity and checking or popping the largest |
| 57 | +element is `O(1)`. Converting a vector to a priority queue can be done |
| 58 | +in-place, and has `O(n)` complexity. A priority queue can also be converted to |
| 59 | +a sorted vector in-place, allowing it to be used for an `O(n log n)` in-place |
| 60 | +heapsort. |
| 61 | + |
| 62 | +# Iterators |
| 63 | + |
| 64 | +## Iteration protocol |
| 65 | + |
| 66 | +The iteration protocol is defined by the `Iterator` trait in the |
| 67 | +`std::iterator` module. The minimal implementation of the trait is a `next` |
| 68 | +method, yielding the next element from an iterator object: |
| 69 | + |
| 70 | +~~~ |
| 71 | +/// An infinite stream of zeroes |
| 72 | +struct ZeroStream; |
| 73 | +
|
| 74 | +impl Iterator<int> for ZeroStream { |
| 75 | + fn next(&mut self) -> Option<int> { |
| 76 | + Some(0) |
| 77 | + } |
| 78 | +} |
| 79 | +~~~~ |
| 80 | +
|
| 81 | +Reaching the end of the iterator is signalled by returning `None` instead of |
| 82 | +`Some(item)`: |
| 83 | +
|
| 84 | +~~~ |
| 85 | +/// A stream of N zeroes |
| 86 | +struct ZeroStream { |
| 87 | + priv remaining: uint |
| 88 | +} |
| 89 | + |
| 90 | +impl ZeroStream { |
| 91 | + fn new(n: uint) -> ZeroStream { |
| 92 | + ZeroStream { remaining: n } |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +impl Iterator<int> for ZeroStream { |
| 97 | + fn next(&mut self) -> Option<int> { |
| 98 | + if self.remaining == 0 { |
| 99 | + None |
| 100 | + } else { |
| 101 | + self.remaining -= 1; |
| 102 | + Some(0) |
| 103 | + } |
| 104 | + } |
| 105 | +} |
| 106 | +~~~ |
| 107 | +
|
| 108 | +## Container iterators |
| 109 | +
|
| 110 | +Containers implement iteration over the contained elements by returning an |
| 111 | +iterator object. For example, vectors have four iterators available: |
| 112 | +
|
| 113 | +* `vector.iter()`, for immutable references to the elements |
| 114 | +* `vector.mut_iter()`, for mutable references to the elements |
| 115 | +* `vector.rev_iter()`, for immutable references to the elements in reverse order |
| 116 | +* `vector.mut_rev_iter()`, for mutable references to the elements in reverse order |
| 117 | +
|
| 118 | +### Freezing |
| 119 | +
|
| 120 | +Unlike most other languages with external iterators, Rust has no *iterator |
| 121 | +invalidation*. As long an iterator is still in scope, the compiler will prevent |
| 122 | +modification of the container through another handle. |
| 123 | +
|
| 124 | +~~~ |
| 125 | +let mut xs = [1, 2, 3]; |
| 126 | +{ |
| 127 | + let _it = xs.iter(); |
| 128 | + |
| 129 | + // the vector is frozen for this scope, the compiler will statically |
| 130 | + // prevent modification |
| 131 | +} |
| 132 | +// the vector becomes unfrozen again at the end of the scope |
| 133 | +~~~ |
| 134 | +
|
| 135 | +These semantics are due to most container iterators being implemented with `&` |
| 136 | +and `&mut`. |
| 137 | +
|
| 138 | +## Iterator adaptors |
| 139 | +
|
| 140 | +The `IteratorUtil` trait implements common algorithms as methods extending |
| 141 | +every `Iterator` implementation. For example, the `fold` method will accumulate |
| 142 | +the items yielded by an `Iterator` into a single value: |
| 143 | +
|
| 144 | +~~~ |
| 145 | +let xs = [1, 9, 2, 3, 14, 12]; |
| 146 | +let result = xs.iter().fold(0, |accumulator, item| accumulator - *item); |
| 147 | +assert_eq!(result, -41); |
| 148 | +~~~ |
| 149 | +
|
| 150 | +Some adaptors return an adaptor object implementing the `Iterator` trait itself: |
| 151 | +
|
| 152 | +~~~ |
| 153 | +let xs = [1, 9, 2, 3, 14, 12]; |
| 154 | +let ys = [5, 2, 1, 8]; |
| 155 | +let sum = xs.iter().chain_(ys.iter()).fold(0, |a, b| a + *b); |
| 156 | +assert_eq!(sum, 57); |
| 157 | +~~~ |
| 158 | +
|
| 159 | +Note that some adaptors like the `chain_` method above use a trailing |
| 160 | +underscore to work around an issue with method resolve. The underscores will be |
| 161 | +dropped when they become unnecessary. |
| 162 | +
|
| 163 | +## For loops |
| 164 | +
|
| 165 | +The `for` loop syntax is currently in transition, and will switch from the old |
| 166 | +closure-based iteration protocol to iterator objects. For now, the `advance` |
| 167 | +adaptor is required as a compatibility shim to use iterators with for loops. |
| 168 | +
|
| 169 | +~~~ |
| 170 | +let xs = [2, 3, 5, 7, 11, 13, 17]; |
| 171 | + |
| 172 | +// print out all the elements in the vector |
| 173 | +for xs.iter().advance |x| { |
| 174 | + println(x.to_str()) |
| 175 | +} |
| 176 | + |
| 177 | +// print out all but the first 3 elements in the vector |
| 178 | +for xs.iter().skip(3).advance |x| { |
| 179 | + println(x.to_str()) |
| 180 | +} |
| 181 | +~~~ |
| 182 | +
|
| 183 | +For loops are *often* used with a temporary iterator object, as above. They can |
| 184 | +also advance the state of an iterator in a mutable location: |
| 185 | +
|
| 186 | +~~~ |
| 187 | +let xs = [1, 2, 3, 4, 5]; |
| 188 | +let ys = ["foo", "bar", "baz", "foobar"]; |
| 189 | + |
| 190 | +// create an iterator yielding tuples of elements from both vectors |
| 191 | +let mut it = xs.iter().zip(ys.iter()); |
| 192 | + |
| 193 | +// print out the pairs of elements up to (&3, &"baz") |
| 194 | +for it.advance |(x, y)| { |
| 195 | + println(fmt!("%d %s", *x, *y)); |
| 196 | + |
| 197 | + if *x == 3 { |
| 198 | + break; |
| 199 | + } |
| 200 | +} |
| 201 | + |
| 202 | +// yield and print the last pair from the iterator |
| 203 | +println(fmt!("last: %?", it.next())); |
| 204 | + |
| 205 | +// the iterator is now fully consumed |
| 206 | +assert!(it.next().is_none()); |
| 207 | +~~~ |
0 commit comments