-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Tracking Issue for Iterator::try_collect
#94047
Comments
The name |
Maybe I'm blind and I can't find it, but you can already to something like this: fn main() {
let a = vec![Some(3), Some(4)];
let b = vec![Some(3), None];
let c = vec![Result::<_, ()>::Ok(3), Ok(4)];
let d = vec![Ok(3), Err(())];
println!("{:?}", a.into_iter().collect::<Option<Vec<_>>>());
println!("{:?}", b.into_iter().collect::<Option<Vec<_>>>());
println!("{:?}", c.into_iter().collect::<Result<Vec<_>, _>>());
println!("{:?}", d.into_iter().collect::<Result<Vec<_>, _>>());
} What is the difference to |
IIRC the point was to ease the discovery of the method. |
Right, the name of this function made me think it was about collecting regular (non- |
@hellow554 the main differences are:
Perhaps I wasn't clear enough about the difference between
Yeah, I see how that might become a conflict; I guess there's two distinct ways that collecting can fail. If anyone has suggestions for method names that are clearer, I'd love to hear them. |
On the point about |
@a-lafrance thanks for that explanation. That helps me understanding your intent. And yes, IMHO that should be in the original post as a description :) @TrolledWoods That was my first thought too, why don't you just use |
@hellow554 If there is an argument for them to take |
Yeah, given that the |
Note that rust/library/core/benches/iter.rs Lines 150 to 158 in f838a42
It's absolutely by design that anything which can early-exit takes There's nothing wrong with resuming iteration after getting an error from |
Oh that's an interesting point @scottmcm. I was wondering why some methods took |
Can this situation be improved using specialization? I.e. specialize |
Thinking about it more there's also the argument that &mut self signifies that it's intended to sometimes resume the value, whereas if it's self by value, you should expect weird behaviour when resuming, so it may be a better idea, yeah. Surprising that by_ref is a performance footgun though, that feels like something that should be fixed asap |
I probably shouldn't have used a word as strong as "footgun" for this. If you're just iterating over, say, a slice, then it makes no difference at all. It's only in certain cases -- So it's very important in I don't know whether specialization in currently in a place where it could fix it. |
Just a thought: I was looking back at Rust by Example recently and I noticed that this section mentions fallible collection with |
Let `try_collect` take advantage of `try_fold` overrides No public API changes. With this change, `try_collect` (rust-lang#94047) is no longer going through the `impl Iterator for &mut impl Iterator`, and thus will be able to use `try_fold` overrides instead of being forced through `next` for every element. Here's the test added, to see that it fails before this PR (once a new enough nightly is out): https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=462f2896f2fed2c238ee63ca1a7e7c56 This might as well go to the same person as my last `try_process` PR (rust-lang#93572), so r? `@yaahc`
Let `try_collect` take advantage of `try_fold` overrides No public API changes. With this change, `try_collect` (rust-lang#94047) is no longer going through the `impl Iterator for &mut impl Iterator`, and thus will be able to use `try_fold` overrides instead of being forced through `next` for every element. Here's the test added, to see that it fails before this PR (once a new enough nightly is out): https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=462f2896f2fed2c238ee63ca1a7e7c56 This might as well go to the same person as my last `try_process` PR (rust-lang#93572), so r? ``@yaahc``
…llect The Rust team has begun to introduce try_collect. I will keep an eye on this implementation and revisit this, but for the time being, I'm going to disambiguate this so that I can move on without worrying about a future breakage. - rust-lang/rust#94047 - https://doc.rust-lang.org/nightly/std/iter/trait.Iterator.html#method.try_collect
This comment was marked as off-topic.
This comment was marked as off-topic.
I'd like to share a benefit from An example risingwavelabs/risingwave#3081 |
First of all, thank you for this, I needed that snippet. Also, if this ever gets merged, it'd be nice to have a clippy lint so I don't have to remember to remove where I've copied and pasted that snippet all over my codebase. :3 |
I think the iterator methods prefixed with If collecting can fail due to a |
It's not specific to reservation of |
I don't like the name Based on its name, I would've expected This seems like an unnecessary generalization of the idea of being able to It might seem a more clunky name, but this seems like it should be named |
(NOT A CONTRIBUTION) It is my strongly held position that this API should be removed from the standard library. Its usefulness is far far less than the cognitive load it brings and therefore it does not carry its weight. The biggest issue with this API is that its signature is completely unreasonable. Return And the motivation to add this API to std is not compelling at all. From the API docs and this thread I see:
Remember that collect is just a convenient sugar over a for loop. For that final stated advantage, I would strongly prefer just writing a for loop that doesn't short circuit on the error case. When contrasting these benefits with the signature of this function, I do not have any doubt that this is not a motivation that justifies a signature like this. When you consider other problems brought up in this thread - like that it takes a name that could plausibly useful for a fallibly allocating collect API, which would presumably have a simpler signature and a more compelling motivation - I am honestly shocked that this API has been added to std. |
The Another potential issue is that a hypothetical faillibly-allocating collection function might want to be called |
What use cases are served by |
The try_collect method allows you to collect results from an iterator, where each item in the iterator is a Result. If an error occurs during the iteration, try_collect will return the error immediately. Which is useful in situations where you have a collection of results and you want to handle all the errors together. By using it you can avoid having to manually handle errors in the loop that processes the results. |
@WhyNotHugo I think the docs on itertools's
It's just a convenience alias :) If But, for example, this doesn't compile: fn process_data(data: &[&str]) -> anyhow::Result<()> {
let numbers: Vec<u64> = data.iter().map(|s| s.parse()).collect()?;
println!("processing {numbers:?}");
Ok(())
} (Imagine the We just want to collect numbers into a vector, and propagate any parsing errors up. It doesn't work even if we change the return type of the function to explicitly be Some alternatives that do compile are:
But none of these options are as readable as the first snippet IMO. And that's the appeal of Using itertools' let numbers: Vec<u64> = data.iter().map(|s| s.parse()).try_collect()?; |
BTW, i understand there's a naming issue with If we want to bikeshed on possible method names, may i suggest either:
I personally prefer the latter. It just looks vanilla and obvious :) |
While the name is being bikeshed, here's my two cents: I would prefer some other name than I think this intuition is because of how there is the infallible As for the alternative name, I don't have a strong preference. Either of epidemian's suggestions are fine. |
Since |
Which is why I suggested the name |
Feature gate:
#![feature(iterator_try_collect)]
This is a tracking issue for adding the
try_collect()
method to theIterator
trait, as originally discussed here.Iterator::try_collect()
is a fallible variation ofIterator::collect()
analogous to similar methods forreduce
andfold
, among others, that provides a simpler, generic way to collect iterators ofTry
elements intoTry
-wrapped types.Difference from
Iterator::collect
In terms of functionality, the main difference between
try_collect()
andcollect()
is thattry_collect()
allows you to fallibly collect iterators yielding types that implementTry
but notFromIterator
, whichcollect()
can't do. Concretely this means you cantry_collect()
iterators yieldingControlFlow<_, i32>
intoControlFlow<_, Vec<i32>>
, which you can't do withcollect()
.It's also a
&mut self
method instead ofcollect
'sself
, so that you can resume iteration after it early exits on an error. (Like howtry_fold
is&mut self
whilefold
is justself
.)Another benefit of
try_collect()
is discoverability. Because the discoverability of the "collect-into-Result
" appears to be lower than desired, "promoting" the technique into its own method seems like a good way to increase its discoverability and reach more users who would find it useful. (One of many examples of people just not being able to find theOption: FromIterator
themselves: https://users.rust-lang.org/t/try-collect-as-iter-consuming-operation/20479?u=scottmcm.)Finally,
try_collect()
presents a small ergonomics benefit in terms of type hints when collecting, namely that you only have to hint at the output type you're collecting into, not the wholeTry
type. For example, that means you can collect an iterator yieldingResult<i32, SomeComplicatedError>
withtry_collect::<Vec<_>>()
, as opposed to having to specify the wholeResult
withcollect::<Result<Vec<_>, _>>()
.Public API
Steps / History
try_collect()
helper method toIterator
#94041Unresolved Questions
self
rather than&mut self
, to prevent users from accidentally continuing to use the iterator after atry_collect()
failure? Note that you can still continue to use the iterator if you useby_ref()
first, so it's not necessarily a functionality change.try_collect()
conflict too much with the idea of collecting that's fallible in allocation? (i.e. collecting withVec::try_reserve
or similar)The text was updated successfully, but these errors were encountered: