But iterator involves some marker traits (DoubleEndedIterator, ExactSizeIterator, FusedIterator, TrustedLen) and some optionnal methods (size_hint, count, etc.).
Delegate method call involves a lot of boilerplate code but in other side don't do it may prevents optimisations and/or features.
Is there a crate or a way to automagically implement all special traits for a new typed iterator based on the inner iterator ?
Or am I overthinking and implement only Iterator::next is fine ?
I think it would be ok to not create a new struct, as the type signature makes it clear that you're returning something that implements Iterator (which is probably the most important part). Also, the usage of IterWithId would probably be a bit restricted as it contains a reference back up to the &self, so it might not need to be super documented.
One way to make the signature more clear could be something like like (this would still have the reference to &self problem though):
struct PackageWithId<'a>(PackageId, &Package)
and then return a impl Iterator<item = PackageWithId>.
Then you wouldn't have to implement all the other traits.
The standard library returns named iterators where it can because a) it lets you store them in fields, and b) returning impl Iterator wasn't around when Rust hit 1.0.
Nowadays, I would just return impl Iterator<Item = ...> and leave it be. The only possible negative is that people won't be able to store your iterator in a struct without also making that struct generic.
That should be fine though, because I would expect the return value from iter_with_id() to be used as a temporary view into your struct and not something that's long-lived. Your IterWithId also doesn't seem to provide extra helper methods (e.g. std::path::Iter has a as_path() method that lets you see the un-consumed parts of the path), so I would see no benefit to explicitly storing a IterWithId field as opposed to some I: Iterator.
For example, you might write a parser with 1 token of lookahead like this:
I mostly agree, I'd just add that I might want to return impl DoubleEndedIterator<Item = …> or impl ExactSizeIterator<Item = …> instead of justIterator, depending on what it's iterating, so that callers can still have .rev() or .len().
It really depends on how you intend the iterator to be used and whether you might want to make internal refactorings that will modify the bounds of the return type. For example, HashMap::iter() doesn't implement DoubleEndedIterator whereas the iterator BTreeMap::iter() does, so changing self.packages from a BTreeMap to a HashMap may mean you can't return impl DoubleEndedIterator any more.
Or as the Go proverb so eloquently puts it,
The bigger the interface, the weaker the abstraction.
— Rob Pike
Where Iterator gives you just one piece of information (the next() method), DoubleEndedIterator gives you another bit, and returning a named type gives you everything that's publicly accessible.
To be fair, I've yet to encounter a real-life scenario where I wanted to switch from BTreeMap to HashMap or vice versa. It just doesn't come up that often because their purposes are different enough in practice. Not to mention that such switching of containers would break other interfaces, too, simply because they require Hash xor Ord, so no matter which of them changes to the other, there will always be a new public trait requirement on their keys.