Search code examples
rustiterator

Rust Deref vs Implementing Iterators over inner types


Here is a toy struct containing a vector

struct SizedVec {
    items: Vec<u32>,
    cap: usize,
}

I wanted to have iteration functions on SizedVec such that they will work as if I am iterating over items directly.

However, I was not sure which traits I should implement: are Iterator and IntoIterator enough? There are a lot of traits in the docs and they seemed a bit complicated and tedious.

Then I saw that I could implement Deref and DerefMut and get all those functions for free using deref coercion:

Sure enough, after doing this:

impl Deref for SizedVec {
    type Target = Vec<u32>;

    fn deref(&self) -> &Self::Target {
        &self.items
    }
}

impl DerefMut for SizedVec {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.items
    }
}

I can use all the iterators I want. But I want to implement a custom logic for push, but DeRef already gave me a push. But according to the docs I should be fine because apparently the lookup is done for each type in order

impl SizedVec {
    fn from_size(size: usize) -> Self {
        Self {
            items: vec![],
            cap: size,
        }
    }

    fn push(&mut self, item: u32) {
        if self.items.len() < self.cap {
            self.items.push(item);
        }else {
            self.items.pop();
            self.items.push(item);
        }
    }
}

And this seems to work,

fn main () {
    let mut svec = SizedVec::from_size(2); // Custom function I implemented
    svec.push(2);
    svec.push(3);
    svec.push(4);
    svec.iter_mut().for_each(|item| {*item = *item + 2});
    println!("{}", svec); // Items: [4, 6], Cap: 2 (Implemented Display so this works)
}

This all seems to work very well, but my question is: is this a good idea? Will I have problems if i convert my SizedVec to a generic type?

If this is a bad idea, is there a simple way I can get all the iteration/map functionality of the inner vector items onto the enclosing struct ?

Also, are there any performance considerations to this?


Solution

  • Deref is fine since your structure is specifically a vec with additional invariants (so making it usable as a vec seems sensible) but DerefMut seems like a rather bad idea as it will let the caller leak the internal Vec (as a mutable reference) and break your invariants.

    One option might be to Deref to a slice (instead of a vec), in which case DerefMut will also get you a slice, which has pretty severe restrictions and thus won't be able to break your invariants. That will mean you may need to reimplement inherent Vec methods, and won't be able to pass off as a Vec, though.

    An other option would be to not implement DerefMut at all, and have more explicit methods for these. Also WRT iterables, note that you only need to implement IntoIterator and other iterator-yielding methods (.iter(), .iter_mut()): the result can just be whatever iterator the underlying collection returns.

    Now I don't know what you meant by

    However, I was not sure which traits I should implement: are Iterator [...] enough?

    You should not, under any circumstance, implement Iterator directly on your SizedVec. That is a terrible, terrible idea.

    The iterator sub-traits are basically markers, they provide additional features over Iterator (but can't necessarily be implemented by every Iterator type). If you're delegating your iteration proper, the underlying iterator type will have likely have implemented those which are possible.