Uh oh!
There was an error while loading. Please reload this page.
Implement iter::Sum and iter::Product for Option - #58975
Conversation
This is similar to the existing implementation for `Result`. It will take each item into the accumulator unless a `None` is returned.
rust-highfive
commented
Mar 6, 2019
r? @kennytm (rust_highfive has picked a reviewer for you, use r? to override) |
Centril
commented
Mar 6, 2019
r? @SimonSapin |
Previous PR about Sum for Option: #50884 |
jtdowney
commented
Mar 8, 2019
@scottmcm thanks, I missed this PR in my search. If folks still think it shouldn't be in |
scottmcm
commented
Mar 8, 2019
@jtdowney Yours is consistent in behaviour with Result, right? I think the other wasn't, so it's possible the outcome will be different here. |
jtdowney
commented
Mar 8, 2019
Correct, this is PR is consistent with how |
Dylan-DPC-zz
commented
Mar 18, 2019
ping from triage anyone from @rust-lang/libs can review this? |
alexcrichton
commented
Mar 19, 2019
This looks reasonable to me to merge! In that case I'll... @rfcbot fcp merge |
Team member @alexcrichton has proposed to merge this. The next step is review by the rest of the tagged team members: Concerns:
Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
dtolnay
commented
Mar 19, 2019
@rfcbot concern not sold on these impls The use cases that come to mind would all be more clearly written with a for-loop. Could you point out some places in real code where these impls would be beneficial? I can see that part of the motivation is to be consistent with Result's Sum and Product impls. But I find that the behavior of summing an iterator of Result<T, E> into a Result<T, E> is more intuitive to me than the behavior of summing Option<T> into an Option<T>. Looking at the example code from the PR: v.iter().map(|&x:&i32|
if x < 0{None}else{Some(x)}).sum()it would be easy to read this as summing only those items that are Some and ignoring Nones. |
@dtolnay the specific case I hit when I discovered this was missing was taking a string, calling |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Co-Authored-By: jtdowney <jdowney@gmail.com>
If it helps, here is the code I was trying to write: fnis_luhn_valid(value:&str) -> bool{
value
.chars().rev().map(|c| c.to_digit(10)).enumerate().map(|(idx, n)| {if idx % 2 != 0{
n.map(|v| {let v = v *2;if v > 9{
v - 9}else{
v
}})}else{
n
}}).sum::<Option<u32>>().map(|total| total % 10 == 0).unwrap_or(false)}fnmain(){dbg!(is_luhn_valid("4111111111111111"));dbg!(is_luhn_valid("5454545454545454"));}This is an implementation of the luhn check algorithm. |
I think a more inspired example in the documentation would be helpful -- something reasonably representative of the way that you might find this impl used in real code where it is the best solution to the problem being solved. (Not that doc comments inside trait impls end up all that visible. Current rustdoc would bury it pretty thoroughly. But it would serve as long-term documentation of the use cases that we wanted to support by providing the impl.)
I think unfortunately this applies to your Luhn implementation too. It is neat that you were able to find a way to express this all in a single method chain, and only one semicolon, but I think it comes out quite challenging to read. A reader looking to understand the implementation or code reviewer looking to confirm that it is correct would need to:
Here is how I might write this instead. My implementation is the same number of lines (24) but some of them are now blank. I find this one far easier to follow -- curious what you think. Even the signature of Based on this use case I would still side against accepting the new impls. fnis_luhn_valid(input:&str) -> bool{matchluhn_sum(input){Some(sum) => sum % 10 == 0,None => false,}}fnluhn_sum(input:&str) -> Option<u32>{letmut sum = 0;for(idx, ch)in input.chars().rev().enumerate(){let digit = ch.to_digit(10)?;if idx % 2 == 0{
sum += digit;}elseif digit *2 <= 9{
sum += digit *2;}else{
sum += digit *2 - 9;}}Some(sum)}Another possibility which is similarly readable: fnis_luhn_valid(input:&str) -> bool{letmut sum = 0;for(idx, ch)in input.chars().rev().enumerate(){let digit = match ch.to_digit(10){Some(digit) => digit,None => returnfalse,};if idx % 2 == 0{
sum += digit;}elseif digit *2 <= 9{
sum += digit *2;}else{
sum += digit *2 - 9;}}
sum % 10 == 0} |
jtdowney
commented
Mar 20, 2019
I am happy to provide a better doc use case but we will have to agree to disagree about the for loop. You seem to be discounting the number of cases we can't think up in this PR and excusing a clear lack of symmetry in the API with regards to |
Centril
commented
Mar 21, 2019
This seems like the usual matter of preference between an imperative vs. a functional style of writing the code. For example, I don't think doing
The functional style algorithm has
If this is a concern then extract the lambdas to the top level and you will have ~2 levels of indent. What is nice about @jtdowney's version is that it encourages separation of concerns and extraction into smaller steps. |
dtolnay
commented
Mar 21, 2019
Thanks for the feedback -- I will give it some more thought under the understanding that @Centril finds the iterator chain easier to read. For now, registering the comment about example code in the documentation, which I would still like to see: @rfcbot concern more fleshed out example use case in documentation |
moshg
commented
Apr 25, 2019
It might be appropriate to make the RFC of the API guideline about the equation of |
I feel this should only be implemented if there were a single obvious semantics derivable from the types involved, or at least one clearly more intuitive than others. Which doesn't appear to be the case. I, as well, would intuitively expect What I would rather see is separate iterator adapters implemented for (Edit: the latter of course already exists and is spelled |
rfcbot
commented
May 4, 2019
The final comment period, with a disposition to merge, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. The RFC will be merged soon. |
Centril
commented
May 11, 2019
r? @sfackler Good deeds begets more =P |
@Centril@dtolnay I don't undertand why the "not sold on these impl"-s concern was resolved---in particular, the ambiguity around filtering I accept that the filtering implementation may not be ideal either, but I don't get why we should commit to either if ambiguous. It's pretty easy to get the short-circuit behaviour with |
Centril
commented
May 27, 2019
r? @dtolnay |
dtolnay
commented
May 28, 2019
@bors r+ |
bors
commented
May 28, 2019
📌 Commit 422a4c0 has been approved by |
dtolnay
commented
May 28, 2019
Bumped stable attrs to 1.37, since beta is currently 1.36. @bors r+ |
bors
commented
May 28, 2019
📌 Commit 9f8d934 has been approved by |
dtolnay
commented
May 28, 2019
@cristicbz the discussion persuaded me that these impls are valuable and that some people find it the most natural and readable way to express certain real-world logic. I don't know that I would ever use them, but it seems to be a matter of preference in terms of what people's background leads them to find more readable. I don't think there is an ambiguity with filtering because filtering would not match the signature of these impl; it would produce |
…r=dtolnay Implement `iter::Sum` and `iter::Product` for `Option` This is similar to the existing implementation for `Result`. It will take each item into the accumulator unless a `None` is returned. I based a lot of this on rust-lang#38580. From that discussion it didn't seem like this addition would be too controversial or difficult. One thing I still don't understand is picking the values for the `stable` attribute. This is my first non-documentation PR for rust so I am open to any feedback on improvements.
…r=dtolnay Implement `iter::Sum` and `iter::Product` for `Option` This is similar to the existing implementation for `Result`. It will take each item into the accumulator unless a `None` is returned. I based a lot of this on rust-lang#38580. From that discussion it didn't seem like this addition would be too controversial or difficult. One thing I still don't understand is picking the values for the `stable` attribute. This is my first non-documentation PR for rust so I am open to any feedback on improvements.
Rollup of 11 pull requests Successful merges: - #58975 (Implement `iter::Sum` and `iter::Product` for `Option`) - #60542 (Add Step::sub_usize) - #60555 (Implement nth_back for RChunks(Exact)(Mut)) - #60766 (Weak::into_raw) - #61048 (Feature/nth back chunks) - #61191 (librustc_errors: Move annotation collection to own impl) - #61235 (Stabilize bufreader_buffer feature) - #61249 (Rename Place::local to Place::local_or_deref_local) - #61291 (Avoid unneeded bug!() call) - #61294 (Rename `TraitOrImpl` to `Assoc` and `trait_or_impl` to `assoc`.) - #61297 (Remove LLVM instruction stats and other (obsolete) codegen stats.) Failed merges: r? @ghost
timvermeulen
commented
May 29, 2019
I realize I'm late to this, but is there any reason for having the iter.map(|x| x.ok_or(())).sum::<Result<_,_>>().ok() |
This is similar to the existing implementation for
Result. It will take each item into the accumulator unless aNoneis returned.I based a lot of this on #38580. From that discussion it didn't seem like this addition would be too controversial or difficult. One thing I still don't understand is picking the values for the
stableattribute. This is my first non-documentation PR for rust so I am open to any feedback on improvements.