Description
Proposal
Problem statement
Currently when one flat_map
s over a mixture of say Vec
s and Option
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 to itertools::Either
):
fn all_tasks(task_containers: &[TaskContainer]) -> impl Iterator<Task> + '_ {
task_containers
.iter()
.flat_map(|tc| match tc.tasks() {
UpToOne(Some(o)) => std::slice::from_ref(o),
UpToOne(None) => &[],
Many(v) => v.as_slice(),
})
}
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
and as_slice_mut
method to Option<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, another into_slice
method on both Option<&T>
and Option<&mut T>
is advised.
Note that removing the branch compared to match
+ slice::from_ref
requires knowing the offset of the value of a Some(_)
variant. Currently we have two cases: Either the offset is zero because of niche optimization, or we can get the offset via an Option<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 of Option<MaybeUninit<T>>
nor the niche optimization applies, we would temporarily revert to the zero-cost abstraction using a match
+ from_ref
until we have an offset_of!
macro (suggested in this RFC proposal) that also covers enum variants. It is advisable to change the implementation to offset_of!
once available anyway.
Links and related work
PR #105871 has a suggested implementation and links to zulip discussion