Instead of retrieving several items of type T
from a data structure, I want to "push" items to that data structure, and I would like to indicate when I have successfully finished pushing them. These operations may be fallible. Passing iterators or callbacks seem to be a bit complex/annoying in regard to error handling.
I wonder what's the idiomatic approach to solve this problem. I came up with an AntiIter
trait as shown below. Is such a thing commonly used? What are your solutions when wanting to push a couple of items while
- being able to indicate completion,
- being flexible with control flow,
- handling errors properly.
The use case is writing items into a database. I don't want to keep all to-be-written items inside a Vec
as that would mean that all items have to be in memory at the same time). Yet I want to be able to rollback the whole transaction when not all items have been successfully retrieved and stored.
#[derive(Debug)]
enum PushError {} // infallible in this example, but may be used in other cases
// This seems complex:
impl<T> Foo<T> {
fn push_iter<E, I>(&mut self, iterator: I) -> Result<Result<(), PushError>, E>
where
I: Iterator<Item = Result<T, E>>,
{
for res in iterator {
self.data.push(res?);
}
Ok(Ok(())) // double-wrapped `Result`, not nice :-(
}
}
// How about using a trait like this:
trait AntiIter {
type Item;
type Error;
fn next(&mut self, item: Self::Item) -> Result<(), Self::Error>;
fn commit(self) -> Result<(), Self::Error>;
}
(Full Playground with example)
I called this "Anti-Iterator" because it's like an Iterator, but instead of returning items in the next
method, it will consume items.