# Make Vec and String be slices

**URL:** <https://internals.rust-lang.org/t/make-vec-and-string-be-slices/24540>\
**Category:** libs\
**Created:** [August 18, 2026, 9:43pm UTC](https://internals.rust-lang.org/t/make-vec-and-string-be-slices/24540 "2026-08-18T21:43:17Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![Dwarsloeper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dwarsloeper/32/11073_2.png) [@Dwarsloeper](https://internals.rust-lang.org/u/Dwarsloeper)\
**Post date:** [August 18, 2026, 9:43pm UTC](https://internals.rust-lang.org/t/make-vec-and-string-be-slices/24540/1 "2026-08-18T21:43:17Z")

</div>

IMHO Vecs and Strings are wasteful in storing the capacity along with the pointer and length. On 64bit systems this makes them to big to fit into u128 registers, where available. And on every move there is an extra usize to push around.

If we moved the capacity to the heap, we’d save that memory for unallocated Vecs and Strings. And it would not be on the stack again in each frame through which it gets passed down (unless they are optimised to overlap.)

I propose putting the capacity at the beginning, but letting the pointer go to the payload. That way the local part is indistinguishable from a slice or fat reference. I.e. `as_ref()/as_str()` are nops. Only operations that need the capacity, would need to calculate the negative offset.

```txt
         ╭─────┬─────╮
         │ ptr │ len │
         ╰─┬───┴─┬───╯
           ▼ ▼
╭──────────┬──────────╮
│ capacity │██████░░░░│
╰─┬────────┴──────────╯
  │ ▲
  ╰───────────────────╯

```

N.b. not pretending the latter two are pointers, just illustrating what they refer to.

---

<div class="post-metadata">

**Author:** ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)\
**Post date:** [August 18, 2026, 9:55pm UTC](https://internals.rust-lang.org/t/make-vec-and-string-be-slices/24540/2 "2026-08-18T21:55:04Z")

</div>

`Vec` has some documented [guarantees](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#guarantees) that make this hard to change:

> Most fundamentally, `Vec` is and always will be a (pointer, capacity, length) triplet. No more, no less.

Right up front, we'd be stretching this guarantee by moving the capacity inward.

> If [`len`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.len "method std::vec::Vec::len")`==`[`capacity`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.capacity "method std::vec::Vec::capacity"), then a `Vec<T>` can be converted to and from a [`Box<[T]>`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html "struct std::boxed::Box") without reallocating or moving the elements.

That means we would have to store a hidden capacity in `Box<[T]>` too, but this wouldn't play well with unsizing coercion from `Box<[T; N]>`.

[`Vec::from_raw_parts`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.from_raw_parts) also makes it hard to change any of this, because that pointer doesn't _have_ to come from a `Vec` originally.

---

<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:** [August 18, 2026, 10:00pm UTC](https://internals.rust-lang.org/t/make-vec-and-string-be-slices/24540/3 "2026-08-18T22:00:18Z")

</div>

> [@Dwarsloeper](#):
>
> If we moved the capacity to the heap, we’d save that memory for unallocated Vecs and Strings.

This isn't going to change.

That said, if you want this, check out [thin\_vec - Rust](https://docs.rs/thin-vec/latest/thin_vec/)

---

<div class="post-metadata">

**Author:** ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)\
**Post date:** [August 18, 2026, 10:03pm UTC](https://internals.rust-lang.org/t/make-vec-and-string-be-slices/24540/4 "2026-08-18T22:03:02Z")

</div>

If you don't like that `ThinVec` has to dereference its length, you could use [`ThinBox`](https://doc.rust-lang.org/nightly/std/boxed/struct.ThinBox.html), something like:

```rust
struct MyVec {
    data: ThinBox<[MaybeUninit<T>]>,
    len: usize,
}

```

---

<div class="post-metadata">

**Author:** ![Dwarsloeper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dwarsloeper/32/11073_2.png) [@Dwarsloeper](https://internals.rust-lang.org/u/Dwarsloeper)\
**Post date:** [August 18, 2026, 10:45pm UTC](https://internals.rust-lang.org/t/make-vec-and-string-be-slices/24540/5 "2026-08-18T22:45:36Z")

</div>

That guarantee strikes me as weird. If you read it narrowly, it would indeed be a breaking change. On its own it feels like needlessly overpromising an implementation detail.

OTOH I never thought about downcasting a `String` to a `Box`. Those relying on that would of course be bitten if it becomes impossible. Plus it would be a memory leak if one forces it anyway and the capacity gets forgotten. Upcasting the other way round would become impossible without reallocation. Even if that use case is maybe only a tiny fraction of `as_ref()/as_str()`.

---

<div class="post-metadata">

**Author:** ![ais523](https://avatars.discourse-cdn.com/v4/letter/a/a183cd/32.png) [@ais523](https://internals.rust-lang.org/u/ais523)\
**Post date:** [August 18, 2026, 11:56pm UTC](https://internals.rust-lang.org/t/make-vec-and-string-be-slices/24540/6 "2026-08-18T23:56:28Z")

</div>

One awkwardness about this is that Rust often uses memory allocators that were designed for C. C's `free()` function, which deallocates memory, does not take a capacity argument, so the allocator has to store it itself, and a lot of allocators store it just before the allocation.

As such, the current Rust `Vec` layout, when using such an allocator, looks like this: length, pointer, capacity in the `Vec`; and capacity, elements in the memory provided by the allocator. This is of course storing the capacity twice, which is overhead caused by an abstraction (specifically, that Rust doesn't try to look at or care about the implementation details of allocators).

It would be possible to design an allocator that doesn't store the capacity (and relies entirely on the capacity reported by the `Vec` to deallocate correctly), but there is actually not a lot of use for these: they wouldn't work with C (so such allocators wouldn't be very backwards-compatible), and most high-performance allocators work by doing things like packing allocations with the same size into the same memory page, and thus naturally end up knowing the capacity anyway (because they can check their metadata for the page on which the memory is allocated to see what size of allocations are allocated there).

There are other allocator changes that would make Rust more efficient, too. For example, on systems with no memory management unit, any address that has ever been allocated will always be readable; and on systems that do have a memory management unit, it can be configured to cause a given range of memory addresses to read as all-zeroes without actually needing to allocate physical memory backing them, so if the allocator needs to free memory, it can just make it read as all-zeroes (while freeing the backing memory) rather than actually freeing the addresses. This means that it would generally be cheap to make an allocator give a guarantee of "any address that has ever been allocated will be readable for the rest of the program's execution, as long as you discard any value you read from non-allocated memory", and such a guarantee can lead to better optimizations because it allows the compiler to speculatively read from potentially deallocated memory without needing to worry about the potential for segfaults.

---

<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:** [August 19, 2026, 12:52am UTC](https://internals.rust-lang.org/t/make-vec-and-string-be-slices/24540/7 "2026-08-19T00:52:07Z")

</div>

> [@Dwarsloeper](#):
>
> That guarantee strikes me as weird. If you read it narrowly, it would indeed be a breaking change. On its own it feels like needlessly overpromising an implementation detail.

The docs explain why we make those guarantees (that indeed make this change impossible): `Vec` is an _extremely_ fundamental type. _A lot_ of unsafe code relies on it, and without those guarantees would have to reimplement it (or parts of it), likely with a worse (or even unsound) implementation.

---

<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:** [August 19, 2026, 12:58am UTC](https://internals.rust-lang.org/t/make-vec-and-string-be-slices/24540/8 "2026-08-19T00:58:04Z")

</div>

Also, and again due to its fundamental nature, you are almost guaranteed to not be able to improve on its basic layout. Any change you will make can be a gain for some use-cases but will be a regression for others, and due to how widely used it is, such regression isn't acceptable. If some specific layout is better for you, you can use a crate that implements it, there are many for many layouts.

Some possible layouts for example: storing the length & capacity as `u32` (on a 64-bit system), storing both inside the allocation (`ThinVec`), storing the capacity only inside the allocation like you suggest, or others.

Some specific downsides to your proposal:

- LLVM will face more difficulties removing redundant capacity loads, and therefore branches;
- Small vectors (e.g. one byte, relevant mostly to custom allocators) will occupy more memory;
- `Vec<T>` when `T` has a large alignment will occupy more memory;
- And probably more.

---

<div class="post-metadata">

**Author:** ![RalfJung](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ralfjung/32/2415_2.png) [@RalfJung](https://internals.rust-lang.org/u/RalfJung)\
**Post date:** [August 19, 2026, 6:12am UTC](https://internals.rust-lang.org/t/make-vec-and-string-be-slices/24540/9 "2026-08-19T06:12:16Z")

</div>

> [@Dwarsloeper](#):
>
> Plus it would be a memory leak if one forces it anyway and the capacity gets forgotten.

It wouldn't be a leak, it would be UB as the `Box` would later pass the pointer where the elements begin to `free`, which is not the actual beginning of the allocation.

---

<div class="post-metadata">

**Author:** ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)\
**Post date:** [August 20, 2026, 3:40pm UTC](https://internals.rust-lang.org/t/make-vec-and-string-be-slices/24540/10 "2026-08-20T15:40:27Z")

</div>

I usually have cases where either the size of the `Vec` doesn't matter much (local variables, structs that have few instances) or cases where size of the `Vec` matters so much (millions of objects) that I want it as small as possible, and then a single-pointer `ThinVec` is even better. This being half-way in between, neither simplest, nor smallest, seems niche to me. There's `Box<[T]>` that's already a slice if you don't need it growable.

---

<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:** [September 17, 2026, 2:19pm UTC](https://internals.rust-lang.org/t/make-vec-and-string-be-slices/24540/11 "2026-09-17T14:19:29Z")

</div>

`Vec` and `String` have a crucial property: creating an empty instance doesn't allocate memory, and in fact is `const`. Currently this works by creating a structure with zero capacity and a dangling pointer. The zero capacity serves as the marker for a trivial structure. How would it work if you moved capacity on the heap? I believe it just wouldn't be possible. You can't rely on the length, an empty `Vec` can have any capacity. And you can't rely on the specific value of the pointer either. Nothing prevents the dangling pointer from actually containing valid data, and I'm sure that it happens on something like MCU or WASM.

Also, I would expect that having capacity on the heap would be a pessimization in many cases. Capacity is checked on every push. Currently it's a simple local variable load, which is trivial (it can be even stored in a register) and easy to optimize away for repeated pushes. If you put capacity on the heap, then any push requires a memory load, extra cache pollution and is likely harder to optimize.

---

<div class="post-metadata">

**Author:** ![sahnehaeubchen](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/sahnehaeubchen/32/9709_2.png) [@sahnehaeubchen](https://internals.rust-lang.org/u/sahnehaeubchen)\
**Post date:** [September 17, 2026, 2:54pm UTC](https://internals.rust-lang.org/t/make-vec-and-string-be-slices/24540/12 "2026-09-17T14:54:03Z")

</div>

> [@afetisov](#):
>
> The zero capacity serves as the marker for a trivial structure. How would it work if you moved capacity on the heap?

[`thin_vec::ThinVec::new`](https://docs.rs/thin-vec/latest/src/thin_vec/lib.rs.html#545-564) is const and they use a static empty header. AFAIR `BTreeMap` used to do the same thing.

---

<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:** [September 17, 2026, 3:04pm UTC](https://internals.rust-lang.org/t/make-vec-and-string-be-slices/24540/13 "2026-09-17T15:04:14Z")

</div>

Since this was necro'd, here's a concrete example of how this would be worse:

Today if I have a `Box<[u8; 1024]>` I can unsize that to `Box<[u8]>` with no reallocation and no data movement and then convert that to a `Vec<u8>` with no reallocation and no data movement. Similarly the typical way to build up a `Box<[T; N]>` is to `<Vec<T>>::with_capacity(N)`, fill it up, then [`.try_into().unwrap()` it](https://doc.rust-lang.org/std/vec/struct.Vec.html#impl-TryFrom%3CVec%3CT%3E%3E-for-Box%3C%5BT;+N%5D%3E).

If `Vec` had the capacity on the heap, then those `Box` \<-\> `Vec` conversions would _always_ need data movement and would (almost) always need reallocations. The guarantee in the docs is there to say things like "you don't need to worry about that; we've never going to make a change like this".

---

<div class="post-metadata">

**Author:** ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)\
**Post date:** [September 17, 2026, 3:55pm UTC](https://internals.rust-lang.org/t/make-vec-and-string-be-slices/24540/14 "2026-09-17T15:55:58Z")

</div>

> [@afetisov](#):
>
> Nothing prevents the dangling pointer from actually containing valid data,

While that's true of the current `ptr::dangling_mut` (roughly `align as *mut T`), we could instead use the _maximal_ aligned pointer that can never represent an actual allocation, because valid objects always have a "one past the end" pointer. So `align.wrapping_neg() as *mut T` would work as a empty sentinel.

(There could still be _other_ valid data there, of types with lesser alignment, but that's fine.)

---

<div class="post-metadata">

**Author:** ![Ddystopia](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ddystopia/32/10915_2.png) [@Ddystopia](https://internals.rust-lang.org/u/Ddystopia)\
**Post date:** [September 17, 2026, 10:22pm UTC](https://internals.rust-lang.org/t/make-vec-and-string-be-slices/24540/15 "2026-09-17T22:22:41Z")

</div>

If you're talking about a hypothetical vector, I'd allocate with an alignment at least 2 and use bottom bit to say if the poiner is dangling or not. 1 means dangling.

---

<div class="post-metadata">

**Author:** ![tczajka](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tczajka/32/8923_2.png) [@tczajka](https://internals.rust-lang.org/u/tczajka)\
**Post date:** [September 17, 2026, 11:00pm UTC](https://internals.rust-lang.org/t/make-vec-and-string-be-slices/24540/16 "2026-09-17T23:00:49Z")

</div>

No need for tricks with alignment bits. As @sahnehaeubchen has suggested, empty vectors could simply point to a shared `static` structure with 0 capacity.
