Skip to content

Option::as_(mut_)slice and ::into_slice #150

Open

Description

Proposal

Problem statement

Currently when one flat_maps over a mixture of say Vecs and Options, 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions