Reputation: 3233
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?
Upvotes: 1
Views: 947
Reputation: 42492
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.
Upvotes: 5