How do I return an iterator over a 2D array with the enumeration indices included? - iterator

I have a struct containing a 2D array:
struct Block;
struct World {
blocks: [[Block; 10]; 10],
}
How could I write a function which returns an iterator over a 2D array, but with the enumeration indices included?
fn enumerate_blocks(&self) -> impl Iterator<Item = (usize, usize, &Block)>
I managed to write an implementation of the function which just returns an iterator without enumeration indices:
fn blocks(&self) -> impl Iterator<Item = &Block> {
self.blocks.iter().flat_map(|x| x.iter())
}
If I call Iterator::enumerate once, I will get an iterator over (usize, [B; 10])s. What I can do next to get an iterator over (usize, usize, B)s?
I know I could make the function return a custom struct then implement Iterator, like image does, but ideally I would like to avoid this.

If I call Iterator::enumerate once, I will get an iterator over (usize, [B; 10])s. What I can do next to get an iterator over (usize, usize, B)s?
Call Iterator::enumerate on the inner array in the same way, continuing to use Iterator::flat_map to combine them. Use Iterator::map to add the outer index to the inner tuple.
#[derive(Debug, Default)]
struct Block;
#[derive(Debug, Default)]
struct World {
blocks: [[Block; 2]; 3],
}
impl World {
fn blocks(&self) -> impl Iterator<Item = (usize, usize, &Block)> {
self.blocks
.iter()
.enumerate()
.flat_map(|(x, v)| v.iter().enumerate().map(move |(y, v)| (x, y, v)))
}
}
fn main() {
let w = World::default();
for (x, y, v) in w.blocks() {
println!("{}, {}, {:?}", x, y, v)
}
}
0, 0, Block
0, 1, Block
1, 0, Block
1, 1, Block
2, 0, Block
2, 1, Block

Related

Iterating generically over either a map or a vector of 2-tuples

Because of reasons, I want to define a generic function that can iterate over key-value pairs expressed either as a mapping, or as a vector of 2-tuples (or anything else that satisfies IntoIterator<Item=(K, V)>, where K and V are stringy). Concretely, I want this to work:
use std::collections::HashMap;
fn main() {
let vc = vec![
("a", "foo"),
("b", "bar"),
("c", "baz")
];
operate(&vc);
let mut map = HashMap::new();
map.insert("d", "blurf");
map.insert("e", "quux");
map.insert("f", "xyzzy");
operate(&map);
}
I've got a definition of operate that works for the HashMap, but not for the vector:
fn operate<I, K, V>(x: I)
where I: IntoIterator<Item=(K, V)>,
K: AsRef<str>, V: AsRef<str>
{
for (ref k, ref v) in x {
println!("{}: {}", k.as_ref(), v.as_ref());
}
}
The error message I get is
error[E0271]: type mismatch resolving `<&std::vec::Vec<(&str, &str)> as std::iter::IntoIterator>::Item == (_, _)`
--> test.rs:18:5
|
18 | operate(&vc);
| ^^^^^^^ expected reference, found tuple
|
= note: expected type `&(&str, &str)`
= note: found type `(_, _)`
= note: required by `operate`
and I don't understand it at all. For one thing, it seems like it's backwards, and for another, why am I only getting an error for the Vec and not the HashMap?
The function provided by IntoIterator consumes self.
fn into_iter(self) -> Self::IntoIter
In order to allow the use of IntoIterator without consuming the collection, both Vec and HashMap have implementations of IntoIterator for &'a Vec<T> and &'a HashMap<K,V,S>, respectively. However, they are not quite the same.
For the hash map, each Item is a (&K, &V), which does not impose a problem because the code effectively assumes the items as 2-sized tuples of keys and values that coerce to &str. And &&str does indeed coerce to &str.
For the vector, each Item is a &T (thus &(K, V) in this case), but because the function is expecting (K, V) as the iterating item, it is currently unable to deal with items of &(K, V).
As it is, the function works if you move the vector, which yields an IntoIterator where Item = (K, V):
let vc = vec![
("a", "foo"),
("b", "bar"),
("c", "baz")
];
operate(vc);
But what if we want it to work for both collections without consuming any of them? Well, I just devised two solutions.
#1
This one involves hiding the tuple behind a new trait:
/// for stuff that can be turned into a pair of references
trait AsRefPair<K, V> {
fn as_ref_pair(&self) -> (&K, &V);
}
Implementing it for for &(K,V) and (&K,&V):
impl<'a, K, V> AsRefPair<K, V> for (&'a K, &'a V) {
fn as_ref_pair(&self) -> (&K, &V) {
(self.0, self.1)
}
}
impl<'a, K, V> AsRefPair<K, V> for &'a (K, V) {
fn as_ref_pair(&self) -> (&K, &V) {
(&self.0, &self.1)
}
}
And now this function works:
fn operate<I, T, K, V>(x: I)
where I: IntoIterator<Item=T>,
T: AsRefPair<K, V>,
K: AsRef<str>, V: AsRef<str>
{
for p in x {
let (ref k, ref v) = p.as_ref_pair();
println!("{}: {}", k.as_ref(), v.as_ref());
}
}
Playground. It might sound a bit crazy at first, but...!
#2
In this one, just stop working with tuples... and start working with key-values!
trait KeyValue<K, V> {
fn key_value(&self) -> (&K, &V) {
(self.key(), self.value())
}
fn key(&self) -> &K;
fn value(&self) -> &V;
}
impl<K, V> KeyValue<K, V> for (K, V) {
fn key(&self) -> &K {
&self.0
}
fn value(&self) -> &V {
&self.1
}
}
impl<'a, K, V> KeyValue<K, V> for &'a (K, V) {
fn key(&self) -> &K {
&self.0
}
fn value(&self) -> &V {
&self.1
}
}
fn operate<I, T, K, V>(x: I)
where I: IntoIterator<Item=T>,
T: KeyValue<K, V>,
K: AsRef<str>, V: AsRef<str>
{
for p in x {
let (ref k, ref v) = p.key_value();
println!("{}: {}", k.as_ref(), v.as_ref());
}
}
Playground. I find this one a bit more idiomatic.
If you pass to the function operate() an iterator instead of a reference to vector, you can use Iterator adaptors to convert Iterator::Item to what you need:
operate(vc.iter().map(|&(ref a, ref b)| (a, b)));

Iterator returning a reference to itself

I'm aware of Lifetime in Iterator impl, but I'd like some more detail to help me properly understand.
I want to write an infinite Iterator that returns &[0], &[0, 1], &[0, 1, 2], etc... . I'd like to write this:
struct Countings(Vec<usize>);
impl Countings {
fn new() -> Countings { Countings(vec![]) }
}
impl Iterator for Countings {
type Item = &[usize];
fn next(&mut self) -> Option<Self::Item> {
self.0.push(self.0.len());
Some(self.0.as_slice())
}
}
I can't because the type Countings::Item does not have a lifetime.
error[E0106]: missing lifetime specifier
--> src/lib.rs:8:17
|
8 | type Item = &[usize];
| ^ expected lifetime parameter
So I add one. It has to be bound by the impl Iterator. That, in turn, requires a lifetime parameter on struct Countings. So far, I'm here:
struct Countings<'a>(Vec<usize>);
impl<'a> Countings<'a> {
fn new() -> Countings<'a> { Countings(vec![]) }
}
impl<'a> Iterator for Countings<'a> {
type Item = &'a [usize];
fn next(&mut self) -> Option<Self::Item> {
self.0.push(self.0.len());
Some(self.0.as_slice())
}
}
Now I have a different error:
error[E0392]: parameter `'a` is never used
--> src/lib.rs:1:18
|
1 | struct Countings<'a>(Vec<usize>);
| ^^
|
= help: consider removing `'a` or using a marker such as `std::marker::PhantomData`
I seriously consider it:
use std::marker::PhantomData;
struct Countings<'a>(Vec<usize>, PhantomData<&'a [usize]>);
impl<'a> Countings<'a> {
fn new() -> Countings<'a> { Countings(vec![], PhantomData) }
}
impl<'a> Iterator for Countings<'a> {
type Item = &'a [usize];
fn next(&mut self) -> Option<Self::Item> {
self.0.push(self.0.len());
Some(self.0.as_slice())
}
}
but to no avail:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/lib.rs:14:25
|
14 | Some(self.0.as_slice())
| ^^^^^^^^
Question 1: What are the "conflicting requirements"?
Question 2: The answer cited above says that Item must borrow from something that the Iterator wraps. I have read the source for std::slice::Windows which is a good example. However, in my case I want to mutate the Vec each time next() is called. Is that possible?
Question 1: What are the "conflicting requirements"?
The borrow you try to return does not have lifetime 'a, as promised. Rather, it has the same lifetime as self. If the signature for next was written in full, it would be:
fn next<'b>(&'b mut self) -> Option<&'a [usize]>
Returning an Option<&'b [usize]> (with lifetime 'b instead of 'a) would be valid if it weren't for the fact that it violates the contract for the Iterator trait. However, it would freeze self until the result is dropped; i.e. you could not call next twice and use the result of both calls together. That's because each call to next can potentially invalidate the previously returned slices; pushing to a Vec can relocate the storage in memory to make room for additional elements, so the pointers in the slices would no longer be valid.
Question 2: The answer cited above says that Item must borrow from something that the Iterator wraps. I have read the source for std::slice::Windows which is a good example. However, in my case I want to mutate the Vec each time next() is called. Is that possible?
It's not possible to do this with the Iterator trait, so you won't be able to use a for loop on your struct. However, you can do it (with the caveat mentioned above) with an ordinary method.
struct Countings(Vec<usize>);
impl Countings {
fn new() -> Countings { Countings(vec![]) }
fn next<'a>(&'a mut self) -> &'a [usize] {
let item = self.0.len();
self.0.push(item);
self.0.as_slice()
}
}
As Francis mentioned, it is not possible to modify the underlying vector during iteration. However, if you were to somehow have the possibility to specify the iteration bound, then things would be much easier:
You would create the vector [0, 1, 2, ...]
And then create an iterator that returns an ever-growing slice, up to the length of the vector
Just the iterator:
struct EverGrowingIterator<'a, T: 'a> {
slice: &'a [T],
current: usize,
}
impl<'a, T> Iterator for EverGrowingIterator<'a, T> {
type Item = &'a [T];
fn next(&mut self) -> Option<&'a [T]> {
if self.current >= self.slice.len() {
None
} else {
self.current += 1;
Some(&self.slice[0..self.current])
}
}
}
And then:
fn ever_growing<'a, T>(slice: &'a [T]) -> EverGrowingIterator<'a, T> {
EverGrowingIterator { slice: slice, current: 0 }
}
fn main() {
let v = vec![0, 1, 2];
for s in ever_growing(&v) {
println!("{:?}", s);
}
}
Will print:
[0]
[0, 1]
[0, 1, 2]
If you need to adapt this for infinite growth, you need to look into creating a custom container (not a Vec) that will grow while preserving references to previous slices. Something like a RefCell<Vec<Box<[T]>>> could be used.

Rust iterators and looking forward (peek/multipeek)

I am trying to use a pattern with iterators in Rust and falling down somewhere, apparently simple.
I would like to iterate through a container and find an element with a predicate [A] (simple), but then look forward using another predicate and get that value [B] and use [B] to mutate [A] in some way. In this case [A] is mutable and [B] can be immutable; this makes no difference to me, only to the borrow checker (rightly).
It would help to understand this with a simple scenario, so I have added a small snippet to let folk see the issue/attempted goal. I have played with itertools and breaking into for/while loops, although I want to remain as idiomatic as possible.
Silly Example scenario
Lookup an even number, find next number that is divisible by 3 and add to the initial number.
#[allow(unused)]
fn is_div_3(num: &u8) -> bool {
num % 3 == 0
}
fn main() {
let mut data: Vec<u8> = (0..100).collect();
let count = data.iter_mut()
.map(|x| {
if *x % 2 == 0 {
// loop through numbers forward to next is_div_3,
// then x = x + that number
}
true
})
.count();
println!("data {:?}, count was {} ", data, count);
}
playground
Sadly I'm a bit late, but here goes.
It's not totally pretty, but it's not as bad as the other suggestion:
let mut data: Vec<u8> = (1..100).collect();
{
let mut mut_items = data.iter_mut();
while let Some(x) = mut_items.next() {
if *x % 2 == 0 {
let slice = mut_items.into_slice();
*x += *slice.iter().find(|&x| x % 3 == 0).unwrap();
mut_items = slice.iter_mut();
}
}
}
println!("{:?}", data);
gives
[1, 5, 3, 10, 5, 15, 7, 17, 9, 22, ...]
as with Matthieu M.'s solution.
The key is to use mut_items.into_slice() to "reborrow" the iterator, effectively producing a local (and thus safe) clone of the iterator.
Warning: The iterator presented right below is unsafe because it allows one to obtain multiple aliases to a single mutable element; skip to the second part for the corrected version. (It would be alright if the return type contained immutable references).
If you are willing to write your own window iterator, then it becomes quite easy.
First, the iterator in all its gory details:
use std::marker::PhantomData;
struct WindowIterMut<'a, T>
where T: 'a
{
begin: *mut T,
len: usize,
index: usize,
_marker: PhantomData<&'a mut [T]>,
}
impl<'a, T> WindowIterMut<'a, T>
where T: 'a
{
pub fn new(slice: &'a mut [T]) -> WindowIterMut<'a, T> {
WindowIterMut {
begin: slice.as_mut_ptr(),
len: slice.len(),
index: 0,
_marker: PhantomData,
}
}
}
impl<'a, T> Iterator for WindowIterMut<'a, T>
where T: 'a
{
type Item = (&'a mut [T], &'a mut [T]);
fn next(&mut self) -> Option<Self::Item> {
if self.index > self.len { return None; }
let slice: &'a mut [T] = unsafe {
std::slice::from_raw_parts_mut(self.begin, self.len)
};
let result = slice.split_at_mut(self.index);
self.index += 1;
Some(result)
}
}
Invoked on [1, 2, 3] it will return (&[], &[1, 2, 3]) then (&[1], &[2, 3]), ... until (&[1, 2, 3], &[]). In short, it iterates over all the potential partitions of the slice (without shuffling).
Which is safe to use as:
fn main() {
let mut data: Vec<u8> = (1..100).collect();
for (head, tail) in WindowIterMut::new(&mut data) {
if let Some(element) = head.last_mut() {
if *element % 2 == 0 {
if let Some(n3) = tail.iter().filter(|i| *i % 3 == 0).next() {
*element += *n3;
}
}
}
}
println!("{:?}", data);
}
Unfortunately it can also be used as:
fn main() {
let mut data: Vec<u8> = (1..100).collect();
let mut it = WindowIterMut::new(&mut data);
let first_0 = { it.next(); &mut it.next().unwrap().0[0] };
let second_0 = &mut it.next().unwrap().0[0];
println!("{:?} {:?}", first_0 as *const _, second_0 as *const _);
}
which when run print: 0x7f73a8435000 0x7f73a8435000, show-casing that both mutable references alias the same element.
Since we cannot get rid of aliasing, we need to get rid of mutability; or at least defer to interior mutability (Cell here since u8 is Copy).
Fortunately, Cell has no runtime cost, but it does cost a bit in ergonomics (all those .get() and .set()).
I take the opportunity to make the iterator slightly more generic too, and rename it since Window is already a used name for a different concept.
struct FingerIter<'a, T>
where T: 'a
{
begin: *const T,
len: usize,
index: usize,
_marker: PhantomData<&'a [T]>,
}
impl<'a, T> FingerIter<'a, T>
where T: 'a
{
pub fn new(slice: &'a [T]) -> FingerIter<'a, T> {
FingerIter {
begin: slice.as_ptr(),
len: slice.len(),
index: 0,
_marker: PhantomData,
}
}
}
impl<'a, T> Iterator for FingerIter<'a, T>
where T: 'a
{
type Item = (&'a [T], &'a T, &'a [T]);
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.len { return None; }
let slice: &'a [T] = unsafe {
std::slice::from_raw_parts(self.begin, self.len)
};
self.index += 1;
let result = slice.split_at(self.index);
Some((&result.0[0..self.index-1], result.0.last().unwrap(), result.1))
}
}
We use it as a building brick:
fn main() {
let data: Vec<Cell<u8>> = (1..100).map(|i| Cell::new(i)).collect();
for (_, element, tail) in FingerIter::new(&data) {
if element.get() % 2 == 0 {
if let Some(n3) = tail.iter().filter(|i| i.get() % 3 == 0).next() {
element.set(element.get() + n3.get());
}
}
}
let data: Vec<u8> = data.iter().map(|cell| cell.get()).collect();
println!("{:?}", data);
}
On the playpen this prints: [1, 5, 3, 10, 5, 15, 7, 17, 9, 22, ...], which seems correct.

An iterator adaptor implementing an SQL-like RIGHT OUTER JOIN using a HashMap

I'm trying to extend bluss's rust-itertools with SQL-like join iterators. I encountered a particular problem with RIGHT OUTER JOIN using a hash join strategy (the strategy itself is actually very simple).
The iterator adaptor struct takes 2 input iterators of which the second (the right) is loaded into the HashMap. The iteration works as follows:
The item from the left iterator is matched against the map - in case of a match return both items
When the left iterator is exhausted, return the non-matched values from the map
The problem is with the second part where I tried to store the map's Values iterator along with the map to store its iteration state. But as I learned in this answer, it's not possible in rust.
Unfortunately I have no idea how it could be done otherwise.
Here is the complete code for the INNER JOIN adaptor, which does the first part:
use std::collections::HashMap;
use std::hash::Hash;
pub struct HashJoinInner<I, K, V0, V1> where
I: Iterator<Item=(K, V0)>,
K: Hash + Eq,
V1: Clone,
{
left: I,
right: HashMap<K, V1>,
}
impl<I, K, V0, V1> HashJoinInner<I, K, V0, V1> where
I: Iterator<Item=(K, V0)>,
K: Hash + Eq,
V1: Clone,
{
/// Create a `HashJoinInner` iterator.
pub fn new<J>(l: I, r: J) -> Self
where J: Iterator<Item=(K, V1)>
{
let mut hm: HashMap<K, V1> = HashMap::new();
for (k, v) in r {
hm.insert(k, v);
}
HashJoinInner {
left: l,
right: hm,
}
}
}
impl<I, K, V0, V1> Iterator for HashJoinInner<I, K, V0, V1> where
I: Iterator<Item=(K, V0)>,
K: Hash + Eq,
V1: Clone,
{
type Item = (V0, V1);
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.left.next() {
Some((k0, v0)) => match self.right.get(&k0) {
Some(v1) => return Some((v0, Clone::clone(v1))),
None => continue,
},
None => return None,
}
}
}
}
I'll be grateful for any idea.
You cannot store the Values iterator because it contains references to the HashMap. These references could become invalid if you move the map. However, you can consume the HashMap using the into_iter method. That owns all the values of the HashMap and can be moved into a new struct.
Here's a tweaking of your code from the earlier question. This isn't yet a left or right join. There's complexity about the switch from being done with one iterator to finishing off the other iterator.
use std::collections::hash_map::{HashMap, IntoIter};
use std::hash::Hash;
struct Foo<K, V>
where K: Hash + Eq,
V: Clone,
{
iter: IntoIter<K, (V, bool)>,
}
impl<K, V> Foo<K, V>
where K: Hash + Eq,
V: Clone,
{
fn new<I>(it: I) -> Self
where I: Iterator<Item=(K, V)>
{
let mut map = HashMap::new();
for (k, v) in it {
map.insert(k, (v, false));
}
Foo { iter: map.into_iter() }
}
}
impl<K, V> Iterator for Foo<K, V>
where K: Hash + Eq,
V: Clone,
{
type Item = V;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.iter.next() {
Some((_, (v, false))) => return Some(v.clone()),
Some(_) => continue,
None => return None,
}
}
}
}
fn main() {
let it = (0..).zip("AB".chars());
let foo = Foo::new(it);
for v in foo {
println!("{}", v);
}
}
However you don't need to do any of that to get what you want. You can simply create a hashmap and check it as you iterate over the other item. I accidentally created a left outer join, but just flip the arguments to get a right outer join. ^_^
use std::collections::hash_map::HashMap;
use std::hash::Hash;
struct LeftOuterJoin<L, K, RV> {
left: L,
right: HashMap<K, RV>,
}
impl<L, K, RV> LeftOuterJoin<L, K, RV>
where K: Hash + Eq
{
fn new<LI, RI>(left: LI, right: RI) -> Self
where L: Iterator<Item=LI::Item>,
LI: IntoIterator<IntoIter=L>,
RI: IntoIterator<Item=(K, RV)>
{
LeftOuterJoin {
left: left.into_iter(),
right: right.into_iter().collect()
}
}
}
impl<L, K, LV, RV> Iterator for LeftOuterJoin<L, K, RV>
where L: Iterator<Item=(K, LV)>,
K: Hash + Eq,
RV: Clone
{
type Item = (K, LV, Option<RV>);
fn next(&mut self) -> Option<Self::Item> {
match self.left.next() {
Some((k, lv)) => {
let rv = self.right.get(&k);
Some((k, lv, rv.cloned()))
},
None => None,
}
}
}
fn main() {
let mut left = HashMap::new();
left.insert(1, "Alice");
left.insert(2, "Bob");
let mut right = HashMap::new();
right.insert(1, "Programmer");
for (id, name, job) in LeftOuterJoin::new(left.into_iter(), right) {
println!("{} ({}) is a {:?}", name, id, job);
}
}
Thanks to Shepmaster's idea of using std::collections::hash_map::IntoIter I've managed to solve the problem.
Here is the complete solution for RIGHT OUTER JOIN using the hash join strategy:
use std::collections::hash_map::{HashMap, IntoIter,};
use std::mem;
use std::hash::Hash;
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct HashJoinRightOuter<L, K, RV> {
left: L,
map: HashMap<K, (RV, bool)>,
/// exclusion iterator - yields the unmatched values from the map. It is created once the left
/// iterator is exhausted
excl_iter: Option<IntoIter<K, (RV, bool)>>,
}
impl<L, K, RV> HashJoinRightOuter<L, K, RV>
where K: Hash + Eq,
{
/// Create a `HashJoinRightOuter` iterator.
pub fn new<LI, RI>(left: LI, right: RI) -> Self
where L: Iterator<Item=LI::Item>,
LI: IntoIterator<IntoIter=L>,
RI: IntoIterator<Item=(K, RV)>
{
let mut map: HashMap<K, (RV, bool)> = HashMap::new();
for (k, v) in right.into_iter() {
map.insert(k, (v, false));
}
HashJoinRightOuter {
left: left.into_iter(),
map: map,
excl_iter: None,
}
}
/// Moves the map to `self.excl_iter`
///
/// Once the left iterator is exhausted, the info about which keys were matched is complete.
/// To be able to iterate over map's values we need to move it into its `IntoIter`.
fn set_excl_iter(&mut self) {
let map = mem::replace(&mut self.map, HashMap::<K, (RV, bool)>::new());
self.excl_iter = Some(map.into_iter());
}
}
impl<L, K, LV, RV> Iterator for HashJoinRightOuter<L, K, RV>
where L: Iterator<Item=(K, LV)>,
K: Hash + Eq,
RV: Clone,
{
type Item = (Option<LV>, RV);
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.excl_iter {
// the left iterator is not yet exhausted
None => match self.left.next() {
Some((lk, lv)) => match self.map.get_mut(&lk) {
Some(rt) => {
rt.1 = true; // flag as matched
return Some((Some(lv), Clone::clone(&rt.0)))
},
None => continue, // not interested in unmatched left value
},
// the left iterator is exhausted so move the map into `self.excl_iter`.
None => self.set_excl_iter(),
},
// iterate over unmatched values
Some(ref mut r) => match r.next() {
Some((_, (rv, matched))) => {
if !matched {
return Some((None, rv));
} else {
continue;
}
},
None => return None,
}
}
}
}
}
fn main() {
let a = (0..).zip("AB".chars());
let b = (1..).zip("XY".chars());
let mut it = HashJoinRightOuter::new(a, b);
assert_eq!(it.next(), Some((Some('B'), 'X')));
assert_eq!(it.next(), Some((None, 'Y')));
assert_eq!(it.next(), None);
}
At the beginning I failed because I tried to store both the data and it's reference in the same struct, which has no meaning anyway. What I really wanted was to store the data first, do some magic with it and once done, move it into another field to work with its transformation.
This can be used to solve other self-referencing struct problems as well.

Writing an iterator [duplicate]

This question already has answers here:
How to implement Iterator and IntoIterator for a simple struct?
(2 answers)
Closed 4 years ago.
Editor's note: This code example is from a version of Rust prior to 1.0 and is not valid Rust 1.0 code. Updated versions of this code no longer produce an error due to changes how for loops are implemented.
I'm writing a Vector struct in Rust.
pub struct Vector {
pub x: f32,
pub y: f32,
pub z: f32,
curr: uint
}
And I'd like to write a simple iterator for it, so that I can iterate over the elements of the vector. It's occasionally useful, plus I know next to nothing about iterators in Rust.
Here's what I've got at the moment.
impl Iterator<f32> for Vector {
fn next(&mut self) -> Option<f32> {
let new_next : Option<f32> = match self.curr {
0 => Some(self.x),
1 => Some(self.y),
2 => Some(self.z),
_ => None
};
let new_curr = (self.curr + 1) % 4;
mem::replace(&mut self.curr, new_curr);
new_next
}
}
Now ideally I'd like to be able to use this like:
let u = Vector::new(0.0f32, 0.0f32, 0.0f32);
for element in u {
///
}
However, I get the following compiler error:
error: cannot borrow immutable local variable `u` as mutable
So I'm stumped. After a couple hours of Googling, I couldn't come up with anything. I feel like I'm missing something huge.
Are you sure you really want the Vector itself to be an iterator? Usually structures and iterators into them are separate. Consider something like this:
pub struct Vector {
pub x: f32,
pub y: f32,
pub z: f32,
}
pub struct VectorIter<'a> {
vector: &'a Vector,
cur: usize,
}
impl<'a> Iterator for VectorIter<'a> {
type Item = f32;
fn next(&mut self) -> Option<f32> {
let r = match self.cur {
0 => self.vector.x,
1 => self.vector.y,
2 => self.vector.z,
_ => return None,
};
self.cur += 1;
Some(r)
}
}
impl Vector {
fn iter(&self) -> VectorIter {
VectorIter {
vector: self,
cur: 0,
}
}
}
fn main() {
let v = Vector { x: 1.0, y: 2.0, z: 3.0 };
for c in v.iter() {
println!("{}", c);
}
}
Because Vector is very simple, it can derive Copy, and its iterator can take it by value:
#[derive(Copy, Clone)]
pub struct Vector {
pub x: f32,
pub y: f32,
pub z: f32,
}
pub struct VectorIter {
vector: Vector,
cur: usize,
}
impl Iterator for VectorIter {
type Item = f32;
fn next(&mut self) -> Option<f32> {
let r = match self.cur {
0 => self.vector.x,
1 => self.vector.y,
2 => self.vector.z,
_ => return None,
};
self.cur += 1;
Some(r)
}
}
impl Vector {
fn iter(&self) -> VectorIter {
VectorIter {
vector: *self,
cur: 0,
}
}
}
fn main() {
let v = Vector { x: 1.0, y: 2.0, z: 3.0 };
for c in v.iter() {
println!("{}", c);
}
}
This variant is probably better unless your Vector contains something other than coordinates. This variant is more flexible because it does not tie the iterator with the iterable, but on the other hand, exactly because of the same reason it may be undesirable (with Copy you can change the original value, and the iterator won't reflect it; without Copy and with references you won't be able to change the original value at all). The semantics you would want to use heavily depends on your use cases.
Editor's note: This answer is no longer useful as of Rust 1.0 due to changes how the for loop works.
Vladimir Matveev's answer is correct and explains what you should do. I'd like to add on by explaining your error a bit more.
error: cannot borrow immutable local variable u as mutable
When you are iterating over something, you need to mutate something that tracks where you are. In your code, that's done by:
mem::replace(&mut self.curr, new_curr);
And Rust knows which thing you want to mutate because the method signature for next indicates it:
fn next(&mut self) -> Option<f32>
The problem is that your object is not mutable:
let u = Vector::new(0.0f32, 0.0f32, 0.0f32);
If you change your code to
let mut u = Vector::new(0.0f32, 0.0f32, 0.0f32);
I'd expect your code to work as-is. All that being said, a separate iterator is probably the right way to go. However, you'll know more than we do about your code!