# Add \`push\`, \`push\_back\`, \`pop\` & \`pop\_back\` methods to fixed-size arrays

**URL:** <https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049>\
**Category:** libs\
**Created:** [July 20, 2022, 8:24pm UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049 "2022-07-20T20:24:24Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![alonely0](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/alonely0/32/9632_2.png) [@alonely0](https://internals.rust-lang.org/u/alonely0)\
**Post date:** [July 20, 2022, 8:24pm UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/1 "2022-07-20T20:24:24Z")

</div>

Arrays are fixed-size. However, it is possible to initialize a new one with the contents of the other via some unsafe black magic. Even though fixed-size arrays aren't as used as vectors or slices, these new methods could help replace some heap-allocated vectors & vecdeques by pure stack arrays. It's true linked lists would be a much better option than abusing these methods (because of the performance difference of writing a pointer vs 1-2 calls to `memcpy()`), but these are super useful for basic array combination & manipulation.

[Playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=cd38d1fff37e2370544e4c1619474372)

```nohighlight
impl<T, const N: usize> [T; N] {
    fn push<const L: usize>(self, array: [T; L]) -> [T; N + L] {
        unsafe {
            let mut result: [T; N + L] = zeroed(); // no real need to use MaybeUninit
            let dst = &raw mut result; // get ptr
            copy_nonoverlapping(&raw const self, dst.cast(), 1); // copy elements
            let dst = &raw mut result[N..]; // offset ptr by N
            copy_nonoverlapping(&raw const array, dst.cast(), 1); // copy elements
            // avoid drop & deallocation of the copied elements
            forget(self);
            forget(array);
            result
        }
    }
    fn push_back<const L: usize>(self, array: [T; L]) -> [T; N + L] {
        unsafe {
            let mut result: [T; N + L] = zeroed(); // no real need to use MaybeUninit
            let dst = &raw mut result; // get ptr
            copy_nonoverlapping(&raw const array, dst.cast(), 1); // copy elements
            let dst = &raw mut result[L..]; // offset ptr by L
            copy_nonoverlapping(&raw const self, dst.cast(), 1); // copy elements
            // avoid drop & deallocation of the copied elements
            forget(self);
            forget(array);
            result
        }
    }
    fn pop(mut self) -> [T; N - 1] {
        if N == 0 {
            panic!("Index out of bounds. This will become a compile-time error once control flow is supported on constant expressions.")
        }
        unsafe {
            let mut result: [T; N - 1] = zeroed(); // no real need to use MaybeUninit
            let src = &raw const self; // get ptr
            copy_nonoverlapping(src.cast(), &raw mut result, 1); // copy elements
            drop_in_place(&raw mut self[N - 1]); // drop popped element
            forget(self); // avoid drop & deallocation of the copied elements
            result
        }
    }
    fn pop_back(mut self) -> [T; N - 1] {
        if N == 0 {
            panic!("Index out of bounds. This will become a compile-time error once control flow is supported on constant expressions.")
        }
        unsafe {
            let mut result: [T; N - 1] = zeroed(); // no real need to use MaybeUninit
            let src = &raw const self[1..]; // offset ptr by one
            copy_nonoverlapping(src.cast(), &raw mut result, 1); // copy elements
            drop_in_place(&raw mut self[0]); // drop popped element
            forget(self); // avoid drop & deallocation of the copied elements
            result
        }
    }
}

```

---

<div class="post-metadata">

**Author:** ![jdahlstrom](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jdahlstrom/32/3351_2.png) [@jdahlstrom](https://internals.rust-lang.org/u/jdahlstrom)\
**Post date:** [July 20, 2022, 8:52pm UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/2 "2022-07-20T20:52:04Z")

</div>

This is close to [`ArrayVec`](https://docs.rs/arrayvec/latest/arrayvec/struct.ArrayVec.html) and similar types (except they mutate in place; an `ArrayVec` backed by a `[T; N]` starts with size 0 and fixed capacity N). There has been a lot of discussion about adding an `ArrayVec` to the standard library (basically the code is already there but only used internally right now). An immutable variant whose methods return some `[T; N±K]` might be more flexible in some use cases, but on the other hand is not implementable in stable right now unlike `ArrayVec`.

---

<div class="post-metadata">

**Author:** ![alonely0](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/alonely0/32/9632_2.png) [@alonely0](https://internals.rust-lang.org/u/alonely0)\
**Post date:** [July 20, 2022, 8:56pm UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/3 "2022-07-20T20:56:57Z")

</div>

From an uninformed quick glance, ArrayVec looks like a wrapper over `[MaybeUninit<T>; N]` (or perhaps `[Option<T>; N]`), since [it panics if the array is full](https://docs.rs/arrayvec/latest/arrayvec/struct.ArrayVec.html#method.push). My implementation creates a new array of a different size.

---

<div class="post-metadata">

**Author:** ![chrefr](https://avatars.discourse-cdn.com/v4/letter/c/e480ec/32.png) [@chrefr](https://internals.rust-lang.org/u/chrefr)\
**Post date:** [July 20, 2022, 9:05pm UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/4 "2022-07-20T21:05:55Z")

</div>

I don't think the libs team will be happy to block a feature on `generic_const_exprs`, but who knows.

---

<div class="post-metadata">

**Author:** ![jdahlstrom](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jdahlstrom/32/3351_2.png) [@jdahlstrom](https://internals.rust-lang.org/u/jdahlstrom)\
**Post date:** [July 20, 2022, 9:06pm UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/5 "2022-07-20T21:06:24Z")

</div>

Yes, I know, I alluded to the difference but could have been clearer. In many use cases it's fine to have a maximum size, especially given that if the exact size is encoded in the type, you _anyway_ have a statically-enforced maximum size no matter what (ie. you can't push or pop conditionally or in a loop because the type changes every time).

---

<div class="post-metadata">

**Author:** ![alonely0](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/alonely0/32/9632_2.png) [@alonely0](https://internals.rust-lang.org/u/alonely0)\
**Post date:** [July 20, 2022, 9:08pm UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/6 "2022-07-20T21:08:30Z")

</div>

The current implementation works fine with the current state of `generic_const_exprs`, but I'm not comfortable merging this until we can move that runtime bounds check to a compile-time if clause on the return type. The code would become something like:

```nohighlight
fn pop$(_back)?(mut self) -> [T; if N > 0 { N - 1 } else { 0 }] {
    if N == 0 { return [] }
    ...
}

```

Or if we can add where clauses:

```nohighlight
fn pop$(_back)?(mut self) -> [T; N] where N > 0 {
    ...
}

```

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [July 20, 2022, 9:14pm UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/7 "2022-07-20T21:14:43Z")

</div>

> [@chrefr](#):
>
> to block a feature on `generic_const_exprs`

This. Realistically, a plan for how to write the signature on stable is needed before something like this can be accepted into the standard library.

There isn't even an RFC for `generic_const_exprs` yet. At the very least that would be needed.

---

<div class="post-metadata">

**Author:** ![alonely0](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/alonely0/32/9632_2.png) [@alonely0](https://internals.rust-lang.org/u/alonely0)\
**Post date:** [July 20, 2022, 9:16pm UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/8 "2022-07-20T21:16:47Z")

</div>

As long as we can maintain the same signature & behavior as the one already implemented by `generic_const_exprs` I don't see any problem putting this behind a feature gate.

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [July 20, 2022, 9:30pm UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/9 "2022-07-20T21:30:52Z")

</div>

> [@alonely0](#):
>
> any problem putting this behind a feature gate

`std` just doesn't take things where the public interface depends on speculative features, even under feature gates.

You're free to make a nightly-only crate with these for now, and propose them for `std` once the necessary features are far enough along the stability path.

---

<div class="post-metadata">

**Author:** ![alonely0](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/alonely0/32/9632_2.png) [@alonely0](https://internals.rust-lang.org/u/alonely0)\
**Post date:** [July 20, 2022, 9:31pm UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/10 "2022-07-20T21:31:53Z")

</div>

Fair enough

---

<div class="post-metadata">

**Author:** ![alonely0](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/alonely0/32/9632_2.png) [@alonely0](https://internals.rust-lang.org/u/alonely0)\
**Post date:** [July 20, 2022, 11:20pm UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/11 "2022-07-20T23:20:17Z")

</div>

[https://crates.io/crates/array\_manipulation](https://crates.io/crates/array_manipulation)

---

<div class="post-metadata">

**Author:** ![mjbshaw](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mjbshaw/32/5103_2.png) [@mjbshaw](https://internals.rust-lang.org/u/mjbshaw)\
**Post date:** [July 21, 2022, 12:01am UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/12 "2022-07-21T00:01:31Z")

</div>

For popping, could we transmute to a `MaybeUninit` array, drop the popped elements, and then transmute back (at the right offset) to an array of smaller size? That avoids memcpy.

Also, using `zeroed()` could cause UB (consider if `T` is a reference, or a `NonZero*`) and I think `MaybeUninit` should be used (it also avoids an unnecessary memset).

---

<div class="post-metadata">

**Author:** ![alonely0](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/alonely0/32/9632_2.png) [@alonely0](https://internals.rust-lang.org/u/alonely0)\
**Post date:** [July 21, 2022, 12:56am UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/13 "2022-07-21T00:56:33Z")

</div>

> and then transmute back (at the right offset) to an array of smaller size? That avoids memcpy

transmute uses memcpy under the hood

---

<div class="post-metadata">

**Author:** ![afetisov](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/afetisov/32/8508_2.png) [@afetisov](https://internals.rust-lang.org/u/afetisov)\
**Post date:** [July 21, 2022, 1:58am UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/14 "2022-07-21T01:58:29Z")

</div>

I'll just note that, by analogy with `Vec` and slices, these methods should be named something like `append` and `split`, rather than `push_back` and `pop_back`. Maybe `concat` should be used instead of `append`.

Specifically, `push` and `pop` are used only when modifying a single element.

---

<div class="post-metadata">

**Author:** ![alonely0](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/alonely0/32/9632_2.png) [@alonely0](https://internals.rust-lang.org/u/alonely0)\
**Post date:** [July 21, 2022, 3:45am UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/15 "2022-07-21T03:45:03Z")

</div>

> <https://github.com/Alonely0/array_manipulation/commit/a5cb9f64aa47270b4be33e52bd7d7b563d013432>
>
> \* Use \`MaybeUninit\` in order to avoid a \`memset()\` (credit to @mjbshaw)
> \* Implem…ent traits as const where aplicable (using specialization)
> \* Split methods into 2 traits because the compiler wont specialize \`Copy\` & \`Drop\` at the same time. At least that way the methods that dont need the \`Drop\` specialization will get the \`Copy\` one.

---

<div class="post-metadata">

**Author:** ![bascule](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bascule/32/3057_2.png) [@bascule](https://internals.rust-lang.org/u/bascule)\
**Post date:** [July 21, 2022, 10:22pm UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/16 "2022-07-21T22:22:31Z")

</div>

> [@afetisov](#):
>
> Maybe `concat` should be used instead of `append`.

`generic-array` supports a `concat` operation, FWIW:

> **[GenericArray in generic\_array - Rust](https://docs.rs/generic-array/latest/generic_array/struct.GenericArray.html#method.concat)**
>
> Struct representing a generic array - \`GenericArray\<T, N\>\` works like \`\[T; N\]\`

It also has a `split` operation:

> **[GenericArray in generic\_array - Rust](https://docs.rs/generic-array/latest/generic_array/struct.GenericArray.html#method.split)**
>
> Struct representing a generic array - \`GenericArray\<T, N\>\` works like \`\[T; N\]\`

Those names make sense to me. I'd prefer `concat` to `append`.

I thought there was an existing proposal / thread about adding such methods to core arrays, but I can't find it.

---

<div class="post-metadata">

**Author:** ![alonely0](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/alonely0/32/9632_2.png) [@alonely0](https://internals.rust-lang.org/u/alonely0)\
**Post date:** [July 21, 2022, 10:56pm UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/17 "2022-07-21T22:56:28Z")

</div>

> [@bascule](#):
>
> I thought there was an existing proposal / thread about adding such methods to core arrays, but I can't find it.

I also searched for something like this before posting, but found nothing

---

<div class="post-metadata">

**Author:** ![alonely0](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/alonely0/32/9632_2.png) [@alonely0](https://internals.rust-lang.org/u/alonely0)\
**Post date:** [July 21, 2022, 10:56pm UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/18 "2022-07-21T22:56:54Z")

</div>

I'll tallocate some of my time (pun intended) on improving & adding more methods to the repository, with the hope of it being merged to core once constant exprs are mature enough (and maybe specialization too, in order to optimize non-drop & copy arrays).

---

<div class="post-metadata">

**Author:** ![alonely0](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/alonely0/32/9632_2.png) [@alonely0](https://internals.rust-lang.org/u/alonely0)\
**Post date:** [July 22, 2022, 4:39pm UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/19 "2022-07-22T16:39:18Z")

</div>

> <https://github.com/Alonely0/array_manipulation/commit/863fda3512df3f9e3ac46799bda12f617a19b59a>
>
> Some people pointed out on the Pre-RFC that the methods should be renamed since …the implementation is fairly different to what the name was suggesting. Also thanks to that feedback, I added more methods to fit more specific needs & avoid \`memcpy()\`s. Now there are unit tests because I was tired of copy-pasting into the playground.

---

<div class="post-metadata">

**Author:** ![alonely0](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/alonely0/32/9632_2.png) [@alonely0](https://internals.rust-lang.org/u/alonely0)\
**Post date:** [July 22, 2022, 4:41pm UTC](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049/20 "2022-07-22T16:41:55Z")

</div>

I really dislike using const generics on the truncate methods, but the alternative is returning an unsized array `[T]` and then having the user transmute to a manually calculated size...

[Next page](https://internals.rust-lang.org/t/add-push-push-back-pop-pop-back-methods-to-fixed-size-arrays/17049.md?page=2)
