I reached the same conclusion. But I couldn't find out how the iterator would be consuming a and b at the same time with some kind of an IntoIterator implementation
+1 for .scan(). I think is a very plausible solution, but I'm thinking about the time complexity. since .truncate() would relocate to the vector. However, this looks pretty neat
Truncating a vector doesn’t affect its allocation. It should be an O(1) operation of writing to the internal length field followed by calling destructors for the dropped items. In this case, that’s one integer at most.
unsafe {
if len > self.len {
return;
}
let remaining_len = self.len - len;
// ah ha
let s = ptr::slice_from_raw_parts_mut(self.as_mut_ptr().add(len), remaining_len);
self.len = len;
ptr::drop_in_place(s);
}
to be sufficient, because the constraint on Item is already an explicit requirement in the definition of IntoIterator. Why is it necessary to repeat it here? (I can move this to a new question, if more appropriate.)
That's fair. Rust is already super intelligent; I don't pretend it to do even more. I just wanted to make sure that I wasn't missing something. My understanding is that we cannot have IntoCountIter: IntoIterator<IntoIter = CountIter> without having also IntoCountIter: IntoIterator<IntoIter = CountIter, Item = CountIter::Item>, because the constraint in the definition of IntoIterator allows us to deduce the second from the first. I hope this is correct.
In my experience, Rust is extremely conservative in the bounds that it will automatically infer for generic parameters. If CountIter was a concrete type instead of generic, the repeated bound would probably not have been necessary.
The way the compiler seems to work is that it infers everything it can from what you explicitly declare on the impl, and then checks those conclusions against the requirements stated elsewhere. If it can’t prove one of those requirements is true from your statements, it’s a compile error.
Then, these explicit statements become the requirements that other code must prove before it can rely on your impl.
In your example, that’s Trait::In == Trait::Out::Inner.
(I haven’t looked at the compiler internals; this is just my behavioral observations)
If you want to avoid the cognitive burden of learning all the adaptors that Iterator has, I recommend learning to use the full power of closures instead, since it generalizes to many other constructs.
let extracted_vec: Vec<Vec<u8>> = a.iter().copied().map({
let mut b = b.into_iter();
move |size| { /* captures the same thing that `scan` did */
let b = b.by_ref(); /* explicit by `&mut` access within the closure's body */
let elems = b.take(size).collect::<Vec<_>>();
drop(b.next());
elems
}
}).collect();
Note that the title of this thread is inaccurate. The problem as posed in detail, and all the solutions, are for the topic "Iterator that enables skipping over u8s in a Vec". Strings are UTF-8, where each character is one to three bytes. Neither the problem as posed nor the solutions work for UTF-8 characters.