1dd515f273
Add slice methods for indexing via an array of indices. Disclaimer: It's been a while since I contributed to the main Rust repo, apologies in advance if this is large enough already that it should've been an RFC. --- # Update: - Based on feedback, removed the `&[T]` variant of this API, and removed the requirements for the indices to be sorted. # Description This adds the following slice methods to `core`: ```rust impl<T> [T] { pub unsafe fn get_many_unchecked_mut<const N: usize>(&mut self, indices: [usize; N]) -> [&mut T; N]; pub fn get_many_mut<const N: usize>(&mut self, indices: [usize; N]) -> Option<[&mut T; N]>; } ``` This allows creating multiple mutable references to disjunct positions in a slice, which previously required writing some awkward code with `split_at_mut()` or `iter_mut()`. For the bound-checked variant, the indices are checked against each other and against the bounds of the slice, which requires `N * (N + 1) / 2` comparison operations. This has a proof-of-concept standalone implementation here: https://crates.io/crates/index_many Care has been taken that the implementation passes miri borrow checks, and generates straight-forward assembly (though this was only checked on x86_64). # Example ```rust let v = &mut [1, 2, 3, 4]; let [a, b] = v.get_many_mut([0, 2]).unwrap(); std::mem::swap(a, b); *v += 100; assert_eq!(v, &[3, 2, 101, 4]); ``` # Codegen Examples <details> <summary>Click to expand!</summary> Disclaimer: Taken from local tests with the standalone implementation. ## Unchecked Indexing: ```rust pub unsafe fn example_unchecked(slice: &mut [usize], indices: [usize; 3]) -> [&mut usize; 3] { slice.get_many_unchecked_mut(indices) } ``` ```nasm example_unchecked: mov rcx, qword, ptr, [r9] mov r8, qword, ptr, [r9, +, 8] mov r9, qword, ptr, [r9, +, 16] lea rcx, [rdx, +, 8*rcx] lea r8, [rdx, +, 8*r8] lea rdx, [rdx, +, 8*r9] mov qword, ptr, [rax], rcx mov qword, ptr, [rax, +, 8], r8 mov qword, ptr, [rax, +, 16], rdx ret ``` ## Checked Indexing (Option): ```rust pub unsafe fn example_option(slice: &mut [usize], indices: [usize; 3]) -> Option<[&mut usize; 3]> { slice.get_many_mut(indices) } ``` ```nasm mov r10, qword, ptr, [r9, +, 8] mov rcx, qword, ptr, [r9, +, 16] cmp rcx, r10 je .LBB0_7 mov r9, qword, ptr, [r9] cmp rcx, r9 je .LBB0_7 cmp rcx, r8 jae .LBB0_7 cmp r10, r9 je .LBB0_7 cmp r9, r8 jae .LBB0_7 cmp r10, r8 jae .LBB0_7 lea r8, [rdx, +, 8*r9] lea r9, [rdx, +, 8*r10] lea rcx, [rdx, +, 8*rcx] mov qword, ptr, [rax], r8 mov qword, ptr, [rax, +, 8], r9 mov qword, ptr, [rax, +, 16], rcx ret .LBB0_7: mov qword, ptr, [rax], 0 ret ``` ## Checked Indexing (Panic): ```rust pub fn example_panic(slice: &mut [usize], indices: [usize; 3]) -> [&mut usize; 3] { let len = slice.len(); match slice.get_many_mut(indices) { Some(s) => s, None => { let tmp = indices; index_many::sorted_bound_check_failed(&tmp, len) } } } ``` ```nasm example_panic: sub rsp, 56 mov rax, qword, ptr, [r9] mov r10, qword, ptr, [r9, +, 8] mov r9, qword, ptr, [r9, +, 16] cmp r9, r10 je .LBB0_6 cmp r9, rax je .LBB0_6 cmp r9, r8 jae .LBB0_6 cmp r10, rax je .LBB0_6 cmp rax, r8 jae .LBB0_6 cmp r10, r8 jae .LBB0_6 lea rax, [rdx, +, 8*rax] lea r8, [rdx, +, 8*r10] lea rdx, [rdx, +, 8*r9] mov qword, ptr, [rcx], rax mov qword, ptr, [rcx, +, 8], r8 mov qword, ptr, [rcx, +, 16], rdx mov rax, rcx add rsp, 56 ret .LBB0_6: mov qword, ptr, [rsp, +, 32], rax mov qword, ptr, [rsp, +, 40], r10 mov qword, ptr, [rsp, +, 48], r9 lea rcx, [rsp, +, 32] mov edx, 3 call index_many::bound_check_failed ud2 ``` </details> # Extensions There are multiple optional extensions to this. ## Indexing With Ranges This could easily be expanded to allow indexing with `[I; N]` where `I: SliceIndex<Self>`. I wanted to keep the initial implementation simple, so I didn't include it yet. ## Panicking Variant We could also add this method: ```rust impl<T> [T] { fn index_many_mut<const N: usize>(&mut self, indices: [usize; N]) -> [&mut T; N]; } ``` This would work similar to the regular index operator and panic with out-of-bound indices. The advantage would be that we could more easily ensure good codegen with a useful panic message, which is non-trivial with the `Option` variant. This is implemented in the standalone implementation, and used as basis for the codegen examples here and there.
158 lines
3.8 KiB
Rust
158 lines
3.8 KiB
Rust
#![feature(alloc_layout_extra)]
|
|
#![feature(array_chunks)]
|
|
#![feature(array_methods)]
|
|
#![feature(array_windows)]
|
|
#![feature(bigint_helper_methods)]
|
|
#![feature(cell_update)]
|
|
#![feature(const_align_offset)]
|
|
#![feature(const_assume)]
|
|
#![feature(const_align_of_val_raw)]
|
|
#![feature(const_black_box)]
|
|
#![feature(const_bool_to_option)]
|
|
#![feature(const_caller_location)]
|
|
#![feature(const_cell_into_inner)]
|
|
#![feature(const_convert)]
|
|
#![feature(const_hash)]
|
|
#![feature(const_heap)]
|
|
#![feature(const_maybe_uninit_as_mut_ptr)]
|
|
#![feature(const_maybe_uninit_assume_init_read)]
|
|
#![feature(const_nonnull_new)]
|
|
#![feature(const_num_from_num)]
|
|
#![feature(const_pointer_byte_offsets)]
|
|
#![feature(const_pointer_is_aligned)]
|
|
#![feature(const_ptr_as_ref)]
|
|
#![feature(const_ptr_read)]
|
|
#![feature(const_ptr_write)]
|
|
#![feature(const_trait_impl)]
|
|
#![feature(const_likely)]
|
|
#![feature(const_location_fields)]
|
|
#![feature(core_intrinsics)]
|
|
#![feature(core_private_bignum)]
|
|
#![feature(core_private_diy_float)]
|
|
#![feature(dec2flt)]
|
|
#![feature(div_duration)]
|
|
#![feature(duration_consts_float)]
|
|
#![feature(duration_constants)]
|
|
#![feature(exact_size_is_empty)]
|
|
#![feature(extern_types)]
|
|
#![feature(flt2dec)]
|
|
#![feature(fmt_internals)]
|
|
#![feature(float_minimum_maximum)]
|
|
#![feature(future_join)]
|
|
#![feature(generic_assert_internals)]
|
|
#![feature(array_try_from_fn)]
|
|
#![feature(hasher_prefixfree_extras)]
|
|
#![feature(hashmap_internals)]
|
|
#![feature(try_find)]
|
|
#![feature(inline_const)]
|
|
#![feature(is_sorted)]
|
|
#![feature(layout_for_ptr)]
|
|
#![feature(pattern)]
|
|
#![feature(pin_macro)]
|
|
#![feature(sort_internals)]
|
|
#![feature(slice_take)]
|
|
#![feature(slice_from_ptr_range)]
|
|
#![feature(split_as_slice)]
|
|
#![feature(maybe_uninit_uninit_array)]
|
|
#![feature(maybe_uninit_write_slice)]
|
|
#![feature(maybe_uninit_uninit_array_transpose)]
|
|
#![feature(min_specialization)]
|
|
#![feature(numfmt)]
|
|
#![feature(step_trait)]
|
|
#![feature(str_internals)]
|
|
#![feature(std_internals)]
|
|
#![feature(test)]
|
|
#![feature(trusted_len)]
|
|
#![feature(try_blocks)]
|
|
#![feature(try_trait_v2)]
|
|
#![feature(slice_internals)]
|
|
#![feature(slice_partition_dedup)]
|
|
#![feature(iter_advance_by)]
|
|
#![feature(iter_array_chunks)]
|
|
#![feature(iter_collect_into)]
|
|
#![feature(iter_partition_in_place)]
|
|
#![feature(iter_intersperse)]
|
|
#![feature(iter_is_partitioned)]
|
|
#![feature(iter_next_chunk)]
|
|
#![feature(iter_order_by)]
|
|
#![feature(iter_repeat_n)]
|
|
#![feature(iterator_try_collect)]
|
|
#![feature(iterator_try_reduce)]
|
|
#![feature(const_mut_refs)]
|
|
#![feature(const_pin)]
|
|
#![feature(const_waker)]
|
|
#![feature(never_type)]
|
|
#![feature(unwrap_infallible)]
|
|
#![feature(pointer_byte_offsets)]
|
|
#![feature(pointer_is_aligned)]
|
|
#![feature(portable_simd)]
|
|
#![feature(ptr_metadata)]
|
|
#![feature(once_cell)]
|
|
#![feature(option_result_contains)]
|
|
#![feature(unsized_tuple_coercion)]
|
|
#![feature(const_option)]
|
|
#![feature(const_option_ext)]
|
|
#![feature(const_result)]
|
|
#![feature(integer_atomics)]
|
|
#![feature(int_roundings)]
|
|
#![feature(slice_group_by)]
|
|
#![feature(split_array)]
|
|
#![feature(strict_provenance)]
|
|
#![feature(strict_provenance_atomic_ptr)]
|
|
#![feature(trusted_random_access)]
|
|
#![feature(unsize)]
|
|
#![feature(const_array_from_ref)]
|
|
#![feature(const_slice_from_ref)]
|
|
#![feature(waker_getters)]
|
|
#![feature(slice_flatten)]
|
|
#![feature(provide_any)]
|
|
#![feature(utf8_chunks)]
|
|
#![feature(is_ascii_octdigit)]
|
|
#![feature(get_many_mut)]
|
|
#![deny(unsafe_op_in_unsafe_fn)]
|
|
#![deny(fuzzy_provenance_casts)]
|
|
|
|
extern crate test;
|
|
|
|
mod alloc;
|
|
mod any;
|
|
mod array;
|
|
mod ascii;
|
|
mod asserting;
|
|
mod atomic;
|
|
mod bool;
|
|
mod cell;
|
|
mod char;
|
|
mod clone;
|
|
mod cmp;
|
|
mod const_ptr;
|
|
mod convert;
|
|
mod fmt;
|
|
mod future;
|
|
mod hash;
|
|
mod intrinsics;
|
|
mod iter;
|
|
mod lazy;
|
|
mod macros;
|
|
mod manually_drop;
|
|
mod mem;
|
|
mod nonzero;
|
|
mod num;
|
|
mod ops;
|
|
mod option;
|
|
mod panic;
|
|
mod pattern;
|
|
mod pin;
|
|
mod pin_macro;
|
|
mod ptr;
|
|
mod result;
|
|
mod simd;
|
|
mod slice;
|
|
mod str;
|
|
mod str_lossy;
|
|
mod task;
|
|
mod time;
|
|
mod tuple;
|
|
mod unicode;
|
|
mod waker;
|