-
Notifications
You must be signed in to change notification settings - Fork 24
Description
Proposal
Problem statement
There's currently no efficient and safe way to remove from a slice and insert a replacement value at a different location, in general. It's some easy unsafe code, but it's be nice to offer it in a more optimized version than rotate+replace (since rotate, while linear, is more expensive than `memmove).
Motivating examples or use cases
Inspired by the conversation in #t-libs > `remove_insert() for `Vec`, or more generally for `&mut [T]` @ 💬.
They specifically wanted to optimize remove+insert on a Vec-like type, to avoid two large memmoves when the positions are close by. But this can be done on slice instead, making it more generally applicable as a building block plus simplifing the interface from what was originally discussed there.
Solution sketch
impl<T> [T] {
pub fn shift_left<const N: usize>(&mut self, inserted: [T; N]) -> [T; N];
pub fn shift_right<const N: usize>(&mut self, inserted: [T; N]) -> [T; N];
}Example:
let mut a = [1, 2, 3, 4, 5];
assert_eq!(a.shift_left([7, 8]), [1, 2]);
assert_eq!(a, [3, 4, 5, 7, 8]);
let mut a = [1, 2, 3, 4, 5];
assert_eq!(a.shift_right([7, 8]), [4, 5]);
assert_eq!(a, [7, 8, 1, 2, 3]);This would be
- trivially a nop for
N == 0, just like<< 0and>> 0. - panic for
N > self.len(). - same as
self.as_mut_array().replace(inserted)forN == self.len(). - handles the scalar case just fine via
.shift_left([3])without adding overhead. - always implemented with
ptr::readthe returned valuesptr::copy(overlapping!) the middle N placesptr::writethe inserted values- none of which can panic, so there's no droppage concerns.
- named to match the existing
rotate_left+rotate_right, as the same intuitions apply
Alternatives
- Only support a single value
- but that makes it less general as a building block, and supporting arrays doesn't make it any less efficient
- Allow passing in the insert/remove positions
- but that makes the interface more complex (two
usizes mean potential confusion over which is which), isn't necessary (since one can always sub-slice the slice), and would add cost in the implementation (which would need to compare the positions and branch to decide what to do)
- but that makes the interface more complex (two
- Do nothing
- certainly always possible, since others can write this themselves, but I do think a "safe memmove" is a valuable offering for
core
- certainly always possible, since others can write this themselves, but I do think a "safe memmove" is a valuable offering for
Links and related work
What happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
- We think this problem seems worth solving, and the standard library might be the right place to solve it.
- We think that this probably doesn't belong in the standard library.
Second, if there's a concrete solution:
- We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
- We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.