From 88c149195af66c95d94db30bc55de6204ad82f8f Mon Sep 17 00:00:00 2001 From: blake2-ppc <blake2-ppc> Date: Mon, 12 Aug 2013 20:24:05 +0200 Subject: [PATCH] std: Replace map_vec, map_vec2, iter_vec2 in std::result Replace these with three functions based on iterators: collect, fold, and fold_. The mapping part is replaced by iterator .map(), so the part that these functions do is to accumulate the final Result<,> value. * `result::collect` gathers `Iterator<Result<V, U>>` to `Result<~[V], U>` * `result::fold` folds `Iterator<Result<T, E>>` to `Result<V, E>` * `result::fold_` folds `Iterator<Result<T, E>>` to `Result<(), E>` --- src/librustc/middle/typeck/infer/combine.rs | 16 ++- src/libstd/result.rs | 139 ++++++++++++-------- 2 files changed, 93 insertions(+), 62 deletions(-) diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs index b1492cac16e..161d96d828b 100644 --- a/src/librustc/middle/typeck/infer/combine.rs +++ b/src/librustc/middle/typeck/infer/combine.rs @@ -68,7 +68,7 @@ use middle::typeck::infer::{InferCtxt, cres, ures}; use middle::typeck::infer::{TypeTrace}; use util::common::indent; -use std::result::{iter_vec2, map_vec2}; +use std::result; use std::vec; use syntax::ast::{Onceness, purity}; use syntax::ast; @@ -275,9 +275,9 @@ pub fn super_tps<C:Combine>( // variance. if vec::same_length(as_, bs) { - iter_vec2(as_, bs, |a, b| { - eq_tys(this, *a, *b) - }).then(|| Ok(as_.to_owned()) ) + result::fold_(as_.iter().zip(bs.iter()) + .map(|(a, b)| eq_tys(this, *a, *b))) + .then(|| Ok(as_.to_owned())) } else { Err(ty::terr_ty_param_size( expected_found(this, as_.len(), bs.len()))) @@ -427,7 +427,8 @@ pub fn super_fn_sigs<C:Combine>( { fn argvecs<C:Combine>(this: &C, a_args: &[ty::t], b_args: &[ty::t]) -> cres<~[ty::t]> { if vec::same_length(a_args, b_args) { - map_vec2(a_args, b_args, |a, b| this.args(*a, *b)) + result::collect(a_args.iter().zip(b_args.iter()) + .map(|(a, b)| this.args(*a, *b))) } else { Err(ty::terr_arg_count) } @@ -586,8 +587,9 @@ pub fn super_tys<C:Combine>( (&ty::ty_tup(ref as_), &ty::ty_tup(ref bs)) => { if as_.len() == bs.len() { - map_vec2(*as_, *bs, |a, b| this.tys(*a, *b) ) - .chain(|ts| Ok(ty::mk_tup(tcx, ts)) ) + result::collect(as_.iter().zip(bs.iter()) + .map(|(a, b)| this.tys(*a, *b))) + .chain(|ts| Ok(ty::mk_tup(tcx, ts)) ) } else { Err(ty::terr_tuple_size( expected_found(this, as_.len(), bs.len()))) diff --git a/src/libstd/result.rs b/src/libstd/result.rs index 9de5e69148a..9ae901d60bc 100644 --- a/src/libstd/result.rs +++ b/src/libstd/result.rs @@ -18,8 +18,7 @@ use either; use iterator::Iterator; use option::{None, Option, Some, OptionIterator}; use vec; -use vec::{OwnedVector, ImmutableVector}; -use container::Container; +use vec::OwnedVector; use to_str::ToStr; use str::StrSlice; @@ -269,86 +268,76 @@ pub fn map_opt<T, U: ToStr, V>(o_t: &Option<T>, } } -// FIXME: #8228 Replaceable by an external iterator? -/// Maps each element in the vector `ts` using the operation `op`. Should an -/// error occur, no further mappings are performed and the error is returned. -/// Should no error occur, a vector containing the result of each map is -/// returned. +/// Takes each element in the iterator: if it is an error, no further +/// elements are taken, and the error is returned. +/// Should no error occur, a vector containing the values of each Result +/// is returned. /// /// Here is an example which increments every integer in a vector, /// checking for overflow: /// -/// fn inc_conditionally(x: uint) -> result<uint,str> { +/// fn inc_conditionally(x: uint) -> Result<uint, &'static str> { /// if x == uint::max_value { return Err("overflow"); } /// else { return Ok(x+1u); } /// } -/// map(~[1u, 2u, 3u], inc_conditionally).chain {|incd| -/// assert!(incd == ~[2u, 3u, 4u]); -/// } +/// let v = [1u, 2, 3]; +/// let res = collect(v.iter().map(|&x| inc_conditionally(x))); +/// assert!(res == Ok(~[2u, 3, 4])); #[inline] -pub fn map_vec<T,U,V>(ts: &[T], op: &fn(&T) -> Result<V,U>) - -> Result<~[V],U> { - let mut vs: ~[V] = vec::with_capacity(ts.len()); - for t in ts.iter() { - match op(t) { - Ok(v) => vs.push(v), - Err(u) => return Err(u) +pub fn collect<T, E, Iter: Iterator<Result<T, E>>>(mut iterator: Iter) + -> Result<~[T], E> { + let (lower, _) = iterator.size_hint(); + let mut vs: ~[T] = vec::with_capacity(lower); + for t in iterator { + match t { + Ok(v) => vs.push(v), + Err(u) => return Err(u) } } - return Ok(vs); + Ok(vs) } -// FIXME: #8228 Replaceable by an external iterator? -/// Same as map, but it operates over two parallel vectors. +/// Perform a fold operation over the result values from an iterator. /// -/// A precondition is used here to ensure that the vectors are the same -/// length. While we do not often use preconditions in the standard -/// library, a precondition is used here because result::t is generally -/// used in 'careful' code contexts where it is both appropriate and easy -/// to accommodate an error like the vectors being of different lengths. +/// If an `Err` is encountered, it is immediately returned. +/// Otherwise, the folded value is returned. #[inline] -pub fn map_vec2<S, T, U: ToStr, V>(ss: &[S], ts: &[T], - op: &fn(&S,&T) -> Result<V,U>) -> Result<~[V],U> { - assert!(vec::same_length(ss, ts)); - let n = ts.len(); - let mut vs = vec::with_capacity(n); - let mut i = 0u; - while i < n { - match op(&ss[i],&ts[i]) { - Ok(v) => vs.push(v), - Err(u) => return Err(u) +pub fn fold<T, V, E, + Iter: Iterator<Result<T, E>>>( + mut iterator: Iter, + mut init: V, + f: &fn(V, T) -> V) + -> Result<V, E> { + for t in iterator { + match t { + Ok(v) => init = f(init, v), + Err(u) => return Err(u) } - i += 1u; } - return Ok(vs); + Ok(init) } -// FIXME: #8228 Replaceable by an external iterator? -/// Applies op to the pairwise elements from `ss` and `ts`, aborting on -/// error. This could be implemented using `map_zip()` but it is more efficient -/// on its own as no result vector is built. +/// Perform a trivial fold operation over the result values +/// from an iterator. +/// +/// If an `Err` is encountered, it is immediately returned. +/// Otherwise, a simple `Ok(())` is returned. #[inline] -pub fn iter_vec2<S, T, U: ToStr>(ss: &[S], ts: &[T], - op: &fn(&S,&T) -> Result<(),U>) -> Result<(),U> { - assert!(vec::same_length(ss, ts)); - let n = ts.len(); - let mut i = 0u; - while i < n { - match op(&ss[i],&ts[i]) { - Ok(()) => (), - Err(u) => return Err(u) - } - i += 1u; - } - return Ok(()); +pub fn fold_<T, E, Iter: Iterator<Result<T, E>>>( + iterator: Iter) + -> Result<(), E> { + fold(iterator, (), |_, _| ()) } + #[cfg(test)] mod tests { use super::*; use either; + use iterator::range; use str::OwnedStr; + use vec::ImmutableVector; pub fn op1() -> Result<int, ~str> { Ok(666) } @@ -431,4 +420,44 @@ mod tests { assert_eq!(r.to_either(), either::Right(100)); assert_eq!(err.to_either(), either::Left(404)); } + + #[test] + fn test_collect() { + assert_eq!(collect(range(0, 0) + .map(|_| Ok::<int, ()>(0))), + Ok(~[])); + assert_eq!(collect(range(0, 3) + .map(|x| Ok::<int, ()>(x))), + Ok(~[0, 1, 2])); + assert_eq!(collect(range(0, 3) + .map(|x| if x > 1 { Err(x) } else { Ok(x) })), + Err(2)); + + // test that it does not take more elements than it needs + let functions = [|| Ok(()), || Err(1), || fail!()]; + + assert_eq!(collect(functions.iter().map(|f| (*f)())), + Err(1)); + } + + #[test] + fn test_fold() { + assert_eq!(fold_(range(0, 0) + .map(|_| Ok::<(), ()>(()))), + Ok(())); + assert_eq!(fold(range(0, 3) + .map(|x| Ok::<int, ()>(x)), + 0, |a, b| a + b), + Ok(3)); + assert_eq!(fold_(range(0, 3) + .map(|x| if x > 1 { Err(x) } else { Ok(()) })), + Err(2)); + + // test that it does not take more elements than it needs + let functions = [|| Ok(()), || Err(1), || fail!()]; + + assert_eq!(fold_(functions.iter() + .map(|f| (*f)())), + Err(1)); + } }