Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing an iterator [duplicate]

Tags:

iterator

rust

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.

like image 660
Chuck Avatar asked Dec 22 '14 10:12

Chuck


People also ask

Can you reuse an Iterator?

iterators are not reusable; you need to get a fresh Iterator from the Iterable collection each time you want to iterate over the elements.

Why are iterators not copyable?

Realize that elements could be mutable (or reference dynamic values) and they may reference other structures and semantics which require a customized copy function. Thus copyable iterators is not orthogonal to copyable stream elements, so this is why copyable iterators are not possible in general.


1 Answers

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.

like image 199
Vladimir Matveev Avatar answered Nov 15 '22 09:11

Vladimir Matveev