-
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 Option::unzip()
#87800
Comments
What is a use case for this? I don't think this can be justified merely by the existence of |
The use case is for turning an option of tuples into a tuple of options, I've ran into needing it a few times |
Simply stating what the function does is not a use case. What is the code, what does it do, and importantly what does the alternative look like? |
My code looked generally like this let (x, y) = if let Some((x, y)) = foo() {
(Some(x), Some(y))
} else {
(None, None)
};
// Use x and y separately With unzip that'd be this let (x, y) = foo().unzip(); The only alternatives to unzip are relatively verbose |
Can you give more context? A use case would show how the function might be used in a real life scenario. It should also demonstrate that the usage cannot be easily refactored to something else. There is no way to know how |
|
Some code I was trying to write today: // Without `.unzip()`
impl<'a, T> Iterator for EdgeIter<'a, T> {
type Item = &'a Edge<T>;
fn next(&mut self) -> Option<Self::Item> {
let edges = self.edges.and_then(|edges| {
if let [edge, rest @ ..] = edges {
Some((rest, edge))
} else {
None
}
});
self.edges = edges.map(|edges| edges.0);
edges.map(|edges| edges.1).and_then(|next_id| {
let next = self.edge_data.get(next_id);
debug_assert!(next.is_some(), "edge data missing edge {}", next_id);
next
})
}
}
// With `.unzip()`
impl<'a, T> Iterator for EdgeIter<'a, T> {
type Item = &'a Edge<T>;
fn next(&mut self) -> Option<Self::Item> {
let (edges, next) = self
.edges
.and_then(|edges| {
if let [edge, rest @ ..] = edges {
Some((rest, edge))
} else {
None
}
})
.unzip();
self.edges = edges;
next.and_then(|next_id| {
let next = self.edge_data.get(next_id);
debug_assert!(next.is_some(), "edge data missing edge {}", next_id);
next
})
}
} |
How about this? fn next(&mut self) -> Option<Self::Item> {
if let Some(edges) = self.edges {
if let [next_id, rest @ ..] = edges {
self.edges = Some(rest);
let next = self.edge_data.get(next_id);
debug_assert!(next.is_some(), "edge data missing edge {}", next_id);
return next;
}
}
None
} |
Doesn't really work, there's a significantly larger amount of work going on that I removed |
Okay, then you still haven't demonstrated the usefulness of |
I don't think its particularly productive to argue over some arbitrary metric of "usefulness" that's entirely subjective since this it may come up more often in some domains compared to others -- if you have concerns with the method w.r.t its interactions with other parts of the language or the implementation, it's much more productive to discuss those. Plus, having the inverse operation for one that already exists seems perfectly reasonable, at least to me.
This comes off quite rude, Kixiron already posted snippets of code with which he found it handy to have, and is not required to copy and paste entire files of code to "prove" its usefulness to you. |
I'm sorry for coming off rude. |
To add a concrete data point, I often run into needing this when dealing with databases. I'll have a nice rusty optional tuple of data that I need to transpose into a tuple of options to then pass into a query. I'd love to see this for N variants. The itertools crate has a |
I was surprised to find myself wanting this today: pub fn map_interpolation(
interp: (&str, Span),
sampling: Option<(crate::Sampling, Span)>,
) -> Result<crate::Interpolation, Error<'_>> {
// Option::unzip is not stable.
let (sampling, sampling_span) = match sampling {
Some((sampling, span)) => (Some(sampling), Some(span)),
None => (None, None),
};
match interp.0 {
"linear" => Ok(crate::Interpolation::Linear(
sampling.unwrap_or(crate::Sampling::Center),
)),
"flat" => {
if let Some(span) = sampling_span {
return Err(Error::SamplingNotPermitted {
interpolation: interp.1,
sampling: span,
});
}
Ok(crate::Interpolation::Flat)
}
"perspective" => Ok(crate::Interpolation::Perspective(
sampling.unwrap_or(crate::Sampling::Center),
)),
_ => Err(Error::UnknownInterpolation(interp.1)),
}
} The two components of |
This comment has been minimized.
This comment has been minimized.
I just ran into this today. I have code like let foo = self.bar.map(|bar| {
let (value, guard) = process(bar);
Some(value, guard)
}); and what I really want to say is let (foo, _guard) = self.bar.map(|bar| {
let (value, guard) = process(bar);
Some(value, guard)
}).unzip(); Having to split this apart myself is annoying. Doing this as a |
Same, I have roughly following code: pub struct Metadata {
pub file_type: Option<String>,
pub file_mime: Option<String>,
...
}
fn get_type_and_mime(path: &str) -> Option<(String, String)>;
fn from_path(path: &str) -> Metadata {
let (file_type, file_mime) = get_type_and_mime(path).map_or( // < this could be replaced by unzip() but you playin
(None, None),
|(t0, t1)| (Some(t0), Some(t1))
);
// ...
Metadata {
file_type,
file_mime,
...
}
} |
Would've found this useful in conjunction with let (host, port) = addr_str.split_once(':').unzip();
let host = host.unwrap_or(&addr_str);
let port = port.map(|p| p.parse::<u16>()).transpose()?; |
I'm unsure I will ever need this cause I also never used zip, but feel very reasonable to me since there have been no remark on the implementation since +6 months I think you can do a PR for a stabilisation @Kixiron |
@rust-lang/libs-api can we start the FCP for this? |
I would like to add that this is very often useful when dealing with functions that return a value pair, which is fairly common. E.g. |
I needed this today, I have: let orig = manager
.get_focused()
.await
.map(|w| (w, manager.get_framed_window(w))); However, I need to use the focused and the framed window separately, which is where |
Proposing stabilization of // core::option
impl<T, U> Option<(T, U)> {
pub fn unzip(self) -> (Option<T>, Option<U>);
} @rfcbot merge |
Team member @Amanieu has proposed to merge this. The next step is review by the rest of the tagged team members: No concerns currently listed. 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. |
I stumbled upon a use case similar to the posts above, and I think this would be useful. 👍 To clarify (and possibly strengthen the argument for stabilizing)... This seems like a natural rationale for why this PR may be accepted, while future a N-tuple |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
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. This will be merged soon. |
Stabilize `Option::unzip()` Stabilizes `Option::unzip()`, closes rust-lang#87800 `@rustbot` modify labels: +T-libs-api
Stabilize `Option::unzip()` Stabilizes `Option::unzip()`, closes rust-lang#87800 ``@rustbot`` modify labels: +T-libs-api
Feature gate:
#![feature(unzip_option)]
This is a tracking issue for the
Option::unzip()
method, which turns anOption<(T, U)>
into an(Option<T>, Option<U>)
.This is the inverse of
Option::zip()
and is a nice little convenience functionPublic API
Steps / History
Option::unzip()
method #87636Option::unzip()
#98204Unresolved Questions
The text was updated successfully, but these errors were encountered: