-
Notifications
You must be signed in to change notification settings - Fork 19
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
Option::as_
(mut_
)slice
and ::into_slice
#150
Comments
As another way of thinking of this: |
One open question is whether to split the |
Option::as_
(mut_
)slice
and ::into_slice
For another motivating example, I happened to notice this in the compiler today: which I'm pretty sure is the |
Yeah, also and as well as (in bootstrap) Should I add another commit to change those implementations (and if I do, will I have to do some |
I'd suggest leaving it alone for now. PRs are easier if they're just library, or just compiler, etc. One can always make a follow-up PR later. |
cc @Amanieu |
I think this is a good API to add. Seconded. |
Thanks @Amanieu. Do you have an opinion on the open question? Should we have |
Hmm I'm actually less certain about the
Perhaps the |
For reference, here's where the discussion started for I still feel it's well motivated, since you can't go from |
I agree there's no perf benefit to having them, and |
Add `Option::as_`(`mut_`)`slice` This adds the following functions: * `Option<T>::as_slice(&self) -> &[T]` * `Option<T>::as_mut_slice(&mut self) -> &[T]` The `as_slice` and `as_mut_slice_mut` functions benefit from an optimization that makes them completely branch-free. ~~Unfortunately, this optimization is not available on by-value Options, therefore the `into_slice` implementations use the plain `match` + `slice::from_ref` approach.~~ Note that the optimization's soundness hinges on the fact that either the niche optimization makes the offset of the `Some(_)` contents zero or the mempory layout of `Option<T>` is equal to that of `Option<MaybeUninit<T>>`. The idea has been discussed on [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Option.3A.3Aas_slice). Notably the idea for the `as_slice_mut` and `into_slice´ methods came from `@cuviper` and `@Sp00ph` hardened the optimization against niche-optimized Options. The [rust playground](https://play.rust-lang.org/?version=nightly&mode=release&edition=2021&gist=74f8e4239a19f454c183aaf7b4a969e0) shows that the generated assembly of the optimized method is basically only a copy while the naive method generates code containing a `test dx, dx` on x86_64. --- EDIT from reviewer: ACP is rust-lang/libs-team#150
Add `Option::as_`(`mut_`)`slice` This adds the following functions: * `Option<T>::as_slice(&self) -> &[T]` * `Option<T>::as_mut_slice(&mut self) -> &[T]` The `as_slice` and `as_mut_slice_mut` functions benefit from an optimization that makes them completely branch-free. ~~Unfortunately, this optimization is not available on by-value Options, therefore the `into_slice` implementations use the plain `match` + `slice::from_ref` approach.~~ Note that the optimization's soundness hinges on the fact that either the niche optimization makes the offset of the `Some(_)` contents zero or the mempory layout of `Option<T>` is equal to that of `Option<MaybeUninit<T>>`. The idea has been discussed on [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Option.3A.3Aas_slice). Notably the idea for the `as_slice_mut` and `into_slice´ methods came from ``@cuviper`` and ``@Sp00ph`` hardened the optimization against niche-optimized Options. The [rust playground](https://play.rust-lang.org/?version=nightly&mode=release&edition=2021&gist=74f8e4239a19f454c183aaf7b4a969e0) shows that the generated assembly of the optimized method is basically only a copy while the naive method generates code containing a `test dx, dx` on x86_64. --- EDIT from reviewer: ACP is rust-lang/libs-team#150
Add `Option::as_`(`mut_`)`slice` This adds the following functions: * `Option<T>::as_slice(&self) -> &[T]` * `Option<T>::as_mut_slice(&mut self) -> &[T]` The `as_slice` and `as_mut_slice_mut` functions benefit from an optimization that makes them completely branch-free. ~~Unfortunately, this optimization is not available on by-value Options, therefore the `into_slice` implementations use the plain `match` + `slice::from_ref` approach.~~ Note that the optimization's soundness hinges on the fact that either the niche optimization makes the offset of the `Some(_)` contents zero or the mempory layout of `Option<T>` is equal to that of `Option<MaybeUninit<T>>`. The idea has been discussed on [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Option.3A.3Aas_slice). Notably the idea for the `as_slice_mut` and `into_slice´ methods came from `@cuviper` and `@Sp00ph` hardened the optimization against niche-optimized Options. The [rust playground](https://play.rust-lang.org/?version=nightly&mode=release&edition=2021&gist=74f8e4239a19f454c183aaf7b4a969e0) shows that the generated assembly of the optimized method is basically only a copy while the naive method generates code containing a `test dx, dx` on x86_64. --- EDIT from reviewer: ACP is rust-lang/libs-team#150
Hmm, a bigger survey might help for deciding on Of the 4 examples mentioned upthread, it appears that at least 3 of them are going through I don't know if there's also a bunch of places that want what |
Add `Option::as_`(`mut_`)`slice` This adds the following functions: * `Option<T>::as_slice(&self) -> &[T]` * `Option<T>::as_mut_slice(&mut self) -> &[T]` The `as_slice` and `as_mut_slice_mut` functions benefit from an optimization that makes them completely branch-free. ~~Unfortunately, this optimization is not available on by-value Options, therefore the `into_slice` implementations use the plain `match` + `slice::from_ref` approach.~~ Note that the optimization's soundness hinges on the fact that either the niche optimization makes the offset of the `Some(_)` contents zero or the mempory layout of `Option<T>` is equal to that of `Option<MaybeUninit<T>>`. The idea has been discussed on [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Option.3A.3Aas_slice). Notably the idea for the `as_slice_mut` and `into_slice´ methods came from `@cuviper` and `@Sp00ph` hardened the optimization against niche-optimized Options. The [rust playground](https://play.rust-lang.org/?version=nightly&mode=release&edition=2021&gist=74f8e4239a19f454c183aaf7b4a969e0) shows that the generated assembly of the optimized method is basically only a copy while the naive method generates code containing a `test dx, dx` on x86_64. --- EDIT from reviewer: ACP is rust-lang/libs-team#150
I could write a lint for the various |
In the meantime I tried github code search to find a few more motivating examples. Here's what I found in a few minutes of searching:
None of those would fit |
Add `Option::as_`(`mut_`)`slice` This adds the following functions: * `Option<T>::as_slice(&self) -> &[T]` * `Option<T>::as_mut_slice(&mut self) -> &[T]` The `as_slice` and `as_mut_slice_mut` functions benefit from an optimization that makes them completely branch-free. ~~Unfortunately, this optimization is not available on by-value Options, therefore the `into_slice` implementations use the plain `match` + `slice::from_ref` approach.~~ Note that the optimization's soundness hinges on the fact that either the niche optimization makes the offset of the `Some(_)` contents zero or the mempory layout of `Option<T>` is equal to that of `Option<MaybeUninit<T>>`. The idea has been discussed on [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Option.3A.3Aas_slice). Notably the idea for the `as_slice_mut` and `into_slice´ methods came from `@cuviper` and `@Sp00ph` hardened the optimization against niche-optimized Options. The [rust playground](https://play.rust-lang.org/?version=nightly&mode=release&edition=2021&gist=74f8e4239a19f454c183aaf7b4a969e0) shows that the generated assembly of the optimized method is basically only a copy while the naive method generates code containing a `test dx, dx` on x86_64. --- EDIT from reviewer: ACP is rust-lang/libs-team#150
Are you still interested in having |
No, I'm fine with what we have now. |
Add `Option::as_`(`mut_`)`slice` This adds the following functions: * `Option<T>::as_slice(&self) -> &[T]` * `Option<T>::as_mut_slice(&mut self) -> &[T]` The `as_slice` and `as_mut_slice_mut` functions benefit from an optimization that makes them completely branch-free. ~~Unfortunately, this optimization is not available on by-value Options, therefore the `into_slice` implementations use the plain `match` + `slice::from_ref` approach.~~ Note that the optimization's soundness hinges on the fact that either the niche optimization makes the offset of the `Some(_)` contents zero or the mempory layout of `Option<T>` is equal to that of `Option<MaybeUninit<T>>`. The idea has been discussed on [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Option.3A.3Aas_slice). Notably the idea for the `as_slice_mut` and `into_slice´ methods came from `@cuviper` and `@Sp00ph` hardened the optimization against niche-optimized Options. The [rust playground](https://play.rust-lang.org/?version=nightly&mode=release&edition=2021&gist=74f8e4239a19f454c183aaf7b4a969e0) shows that the generated assembly of the optimized method is basically only a copy while the naive method generates code containing a `test dx, dx` on x86_64. --- EDIT from reviewer: ACP is rust-lang/libs-team#150
Add `Option::as_`(`mut_`)`slice` This adds the following functions: * `Option<T>::as_slice(&self) -> &[T]` * `Option<T>::as_mut_slice(&mut self) -> &[T]` The `as_slice` and `as_mut_slice_mut` functions benefit from an optimization that makes them completely branch-free. ~~Unfortunately, this optimization is not available on by-value Options, therefore the `into_slice` implementations use the plain `match` + `slice::from_ref` approach.~~ Note that the optimization's soundness hinges on the fact that either the niche optimization makes the offset of the `Some(_)` contents zero or the mempory layout of `Option<T>` is equal to that of `Option<MaybeUninit<T>>`. The idea has been discussed on [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Option.3A.3Aas_slice). Notably the idea for the `as_slice_mut` and `into_slice´ methods came from `@cuviper` and `@Sp00ph` hardened the optimization against niche-optimized Options. The [rust playground](https://play.rust-lang.org/?version=nightly&mode=release&edition=2021&gist=74f8e4239a19f454c183aaf7b4a969e0) shows that the generated assembly of the optimized method is basically only a copy while the naive method generates code containing a `test dx, dx` on x86_64. --- EDIT from reviewer: ACP is rust-lang/libs-team#150
Proposal
Problem statement
Currently when one
flat_map
s over a mixture of sayVec
s andOption
s, there is a hard to find method to create a slice from the latter, that generates suboptimal code.Motivation, use-cases
Consider the following code that is similar to what I came up to when a colleague had the problem of iterating over either an Option or a Vec reference (the colleague, not knowing about
slice::from_ref
, turned toitertools::Either
):Not only is the
std::slice::from_ref
function hard to find, in this case the match introduces a needless branch.Solution sketches
The solution is to add an
as_slice
andas_slice_mut
method toOption<T>
, which can create a slice by copying the data. The methods follow standard API conventions and are negative-cost abstractions (because they are faster than what a normal user would have written).For the sake of completeness, anotherinto_slice
method on bothOption<&T>
andOption<&mut T>
is advised.Note that removing the branch compared to
match
+slice::from_ref
requires knowing the offset of the value of aSome(_)
variant. Currently we have two cases: Either the offset is zero because of niche optimization, or we can get the offset via anOption<MaybeUninit<T>>
, because they are equal with respect to memory layout (I have checked and confirmed that the current layout randomization implementation only works on each variant in isolation and then prepends the discriminant, so this property should hold, and if not, the tests will catch it).Should this change and we get another case, where neither the layout of
Option<T>
is equal to that ofOption<MaybeUninit<T>>
nor the niche optimization applies, we would temporarily revert to the zero-cost abstraction using amatch
+from_ref
until we have anoffset_of!
macro (suggested in this RFC proposal) that also covers enum variants. It is advisable to change the implementation tooffset_of!
once available anyway.Links and related work
PR #105871 has a suggested implementation and links to zulip discussion
The text was updated successfully, but these errors were encountered: