I would like to do something like this:
enum MyEnum {
Foo(Vec<Stuff>),
// ...
}
impl MyEnum {
fn do_sth(&self) -> Result<Bar, E> {
match *self {
MyEnum::Foo(ref vec) => {
let (a, b): (Vec<A>, Vec<B>) = vec
.iter()
.map(|thing| thing.make_a_tuple(arg)) // returns Result<(A, B), E>
.collect::<Result<_, _>>()? // stop on first error
.unzip();
// use a and b
}
// other cases
}
// ...
}
}
This fails to compile with error: the type of this value must be known in this context
.
Through trial and error, I got it to compile like this
fn do_sth(&self) -> Result<Bar, E> {
match *self {
MyEnum::Foo(ref vec) => {
let (a, b): (Vec<A>, Vec<B>) = vec
.iter()
.map(|thing| thing.make_a_tuple(arg))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.unzip();
// use a and b
}
// other cases
}
}
However, I would like to avoid the unnecessary allocation of a Vec<(A, B)>
.
Is it possible to do this without intermediate allocations? I am sure I could do this myself with a loop, but I would prefer to learn the Rusty way.
答案 0 :(得分:3)
我觉得我常常遇到Result
的迭代器遇到这类问题。以至于我在标准库中添加了一个(私有)帮助器类型ResultShunt
。这为Sum
and Product
for Result
s的实施提供了动力。我应该提交有submitted it to itertools所以它可以重复使用:
fn main() {
let iter_source: Vec<Result<(i32, i32), bool>> =
vec![Ok((1, 2)), Err(false), Ok((3, 4))];
let z = ResultShunt::process(iter_source.into_iter(), |iter| {
iter.unzip::<_, _, Vec<_>, Vec<_>>()
});
println!("{:?}", z);
}
// ----- Begin copy-pasta -----
/// An iterator adapter that produces output as long as the underlying
/// iterator produces `Result::Ok` values.
///
/// If an error is encountered, the iterator stops and the error is
/// stored. The error may be recovered later via `reconstruct`.
struct ResultShunt<I, E> {
iter: I,
error: Option<E>,
}
impl<I, T, E> ResultShunt<I, E>
where I: Iterator<Item = Result<T, E>>
{
/// Process the given iterator as if it yielded a `T` instead of a
/// `Result<T, _>`. Any errors will stop the inner iterator and
/// the overall result will be an error.
pub fn process<F, U>(iter: I, mut f: F) -> Result<U, E>
where F: FnMut(&mut Self) -> U
{
let mut shunt = ResultShunt::new(iter);
let value = f(shunt.by_ref());
shunt.reconstruct(value)
}
fn new(iter: I) -> Self {
ResultShunt {
iter: iter,
error: None,
}
}
/// Consume the adapter and rebuild a `Result` value. This should
/// *always* be called, otherwise any potential error would be
/// lost.
fn reconstruct<U>(self, val: U) -> Result<U, E> {
match self.error {
None => Ok(val),
Some(e) => Err(e),
}
}
}
impl<I, T, E> Iterator for ResultShunt<I, E>
where I: Iterator<Item = Result<T, E>>
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
match self.iter.next() {
Some(Ok(v)) => Some(v),
Some(Err(e)) => {
self.error = Some(e);
None
}
None => None,
}
}
}