-
Notifications
You must be signed in to change notification settings - Fork 13.3k
impl Add for Option<T> #75863
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
impl Add for Option<T> #75863
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @kennytm (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
We have discussed about this in https://internals.rust-lang.org/t/lifting-binary-operations-e-g-t-t-option-t-option-t/6618 before, and there's no consensus that we want this impl. It is not natural that (Also, even if we have this impl, your example doesn't work since |
(a, None) => a, | ||
(None, b) => b, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential footgun, it may not be obvious that None
is ignored, I thought None
should become None
at first if either one is None
.
Procedurally, I think this needs an RFC:
But I'm not on T-libs so we'll see what they say. And I'd suggest waiting to get some sort of straw poll from them before putting work into an RFC, since what @kennytm mentioned sounds persuasive to me. |
@kennytm Thanks for the link, that was an insightful discussion. I've found out that there has also been some back and forth on implementing this exact behavior way back in 2013: I was initially convinced that this would be the natural and obvious implementation by drawing a parallel to list/vector concatenation (i.e., Despite that, I feel like the monadic semantics (i.e. fn lift<T0, T1, T2>(f: fn(T0, T1) -> T2) -> impl Fn(Option<T0>, Option<T1>) -> Option<T2> {
move |x, y| Some(f(x?, y?))
}
let add_opt = &lift(std::ops::Add::add);
assert_eq!(add_opt(Some(5), Some(8)), Some(13));
assert_eq!(add_opt(Some(13), None), None);
assert_eq!(vec![Some(3), Some(5), Some(8)].into_iter().fold(Some(0), add_opt), Some(16));
assert_eq!(vec![Some(3), None, Some(8)].into_iter().fold(Some(0), add_opt), None); We can even leverage let xs = vec![Some("he"), Some("llo")];
assert_eq!(xs.into_iter().collect::<Option<String>>(), Some(String::from("hello")));
let xs = vec![Some("he"), None, Some("llo")];
assert_eq!(xs.into_iter().collect::<Option<String>>(), None); Given that the monadic instance of pub trait Accum<Rhs = Self> {
type Output;
fn concat(self, rhs: Rhs) -> Self::Output;
} so that impl<T: Accum<Output = T>> Accum for Option<T> {
type Output = Self;
fn concat(self, other: Self) -> Self::Output {
match (self, other) {
(a, None) => a,
(None, b) => b,
(Some(a), Some(b)) => Some(a.concat(b)),
}
}
} I will close this PR and make a new one if you guys think that's a good idea. |
@zcesur Note that the bar for a new trait in the standard library is quite high. That This PR needed to be to core because it's an impl of a core trait on a core type. If you can go through a new trait, I'd suggest trying it out as a crate first -- you can still implement your trait for Good sleuthing for the old issues. Given that I don't think anything obvious that has changed since then, closing this PR probably sounds like the way to go. |
Sounds good |
Implement
Add
forOption<T>
for allT
that also implAdd
. The resultingOption
type forms a semigroup as well as a monoid given that it also implementsDefault
.Motivation
Fold iterators containing optional values, concatenating the values along the way.
Example
To make the above possible, I've also added an implementation of
Add
forString
along the lines of the below since the existing string impl didn't work. I've just started learning Rust so I don't understand why that is the case. I'd love to learn more if anyone has an explanation!