-
Notifications
You must be signed in to change notification settings - Fork 5
feat: IteratorExt::collect_all
#32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This pairs nicely with `Exn::from_iter`.
|
Thanks for your contribution @80Ltrumpet! I can understand this use case but I can foresee a for-loop solution that is very clear. This fp-style API doesn't read quite fluently to me.
use itertools::Itertools;
use itertools::Either;
let results = vec![ ... ];
let (successes, errors): (Vec<_>, Vec<_>) = results.into_iter().partition_map(|r| match r {
Ok(v) => Either::Left(v),
Err(e) => Either::Right(e),
});@andylokandy what do you think? |
|
Thanks for reviewing, @tisonkun! The advantage of this solution is that it only allocates what is necessary to achieve the goal. The I'm open to any suggestions for improvement, and if you think this fits somewhere else or has a similarly concise expression via some other means (e.g., |
|
I get your point now. Then the semantics of this API is very specific:
Typically, such a specific requirement should be implemented downstream. In my cases, when we need to raise multiple errors, we collect the errors first and call To avoid allocate for Ok variant when the first error occurs: fn process<T, E>(mut results: impl Iterator<Item = Result<T, E>>) -> Result<Vec<T>, Vec<E>> {
let mut successes = vec![];
let mut errors = vec![];
while let Some(result) = results.next() {
match result {
Ok(value) => successes.push(value),
Err(err) => { errors.push(err); break; }
}
}
if errors.is_empty() { return Ok(successes); }
while let Some(result) = results.next() {
if let Err(err) = result {
errors.push(err);
}
}
Err(errors)
}... or simply use your code snippet here. That said, your code snippet is worth adding as an example, but it does not need to be a formal API. In addition, here is some pseudo code on how we use pub fn resolve(
inputs: Inputs
) -> Result<(...), MyError> {
// some other logics
let mut errors = vec![];
for candidate in candidates {
match resolve_sig(...) {
Ok(...) => return Ok(...),
Err(err) => {
errors.push(err.raise(RegistryError {
span,
message: format!("invalid argument for function: {}", candidate.sig),
}));
}
}
}
if errors.is_empty() {
bail!(RegistryError {
span,
message: format!("function not found: {name}({})", args.iter().format(", "))
});
} else {
Err(Exn::from_iter(
errors,
RegistryError {
span,
message: format!(
"no matching function for {name}({})",
args.iter().format(", ")
),
},
))
}
}pub struct ReportFrame {
// ... some other fields
message: String,
children: Vec<ReportFrame>,
}
impl ReportFrame {
pub fn raise(self) -> Exn<ReportError> {
let error = ReportError {
// ...
message: self.message,
};
if self.children.is_empty() {
Exn::new(error)
} else {
Exn::from_iter(self.children.into_iter().map(ReportFrame::raise), error)
}
}
} |
Sounds good to me. Thanks for your consideration! |
This pairs nicely with
Exn::from_iter, so I believe it fits in this crate.