# Could we get guarantee that all pointer-like types have the same representation?

**URL:** <https://internals.rust-lang.org/t/could-we-get-guarantee-that-all-pointer-like-types-have-the-same-representation/24677>\
**Category:** Unsafe Code Guidelines\
**Created:** [October 14, 2026, 10:04pm UTC](https://internals.rust-lang.org/t/could-we-get-guarantee-that-all-pointer-like-types-have-the-same-representation/24677 "2026-10-14T22:04:26Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![Kixunil](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kixunil/32/4110_2.png) [@Kixunil](https://internals.rust-lang.org/u/Kixunil)\
**Post date:** [October 14, 2026, 10:04pm UTC](https://internals.rust-lang.org/t/could-we-get-guarantee-that-all-pointer-like-types-have-the-same-representation/24677/1 "2026-10-14T22:04:26Z")

</div>

I think this code ought to be sound but I couldn't find a good resource to prove so:

```rust
#[repr(C)]
pub struct Foo<B> {
    payload: B,
    version: u8,
}

impl<'a> Borrow<Foo<&'a [u8]>> for Foo<Box<[u8]>> {
    fn borrow(&self) -> &Foo<&'a [u8]> {
        unsafe { &*(self as *const _ as *const Foo<&[u8]>) }
    }
}

```

IIUC it's guaranteed for pointer types to `Sized` objects but not for `Sized` even though I don't see any good reason why `&T`, `&mut T`, `Box<T>`, `*const T`, `*mut T` should have different representations for any `T` - be it `Sized` or not. (Arguably also `Rc<T>` and `Arc<T>` should have same representation but those are library types, so a bit different case.) Miri doesn't complain about this but given it's just a sanitizer I'm not confident it's sufficient.

And if you're wondering why would I want this: to work around the limitation of `HashMap<Foo<Box<[u8]>>, Bar>` which requires `Borrow` for lookups - I want to perform lookup using `Foo<&[u8]>` as the key.

---

<div class="post-metadata">

**Author:** ![programmerjake](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/programmerjake/32/5893_2.png) [@programmerjake](https://internals.rust-lang.org/u/programmerjake)\
**Post date:** [October 14, 2026, 10:35pm UTC](https://internals.rust-lang.org/t/could-we-get-guarantee-that-all-pointer-like-types-have-the-same-representation/24677/2 "2026-10-14T22:35:56Z")

</div>

as a workaround you can use hashbrown which lets you use the [`Equivalent`](https://docs.rs/hashbrown/0.17.1/hashbrown/trait.Equivalent.html) trait to compare `Foo<Box<[u8]>>` directly with `Foo<&[u8]>`

---

<div class="post-metadata">

**Author:** ![programmerjake](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/programmerjake/32/5893_2.png) [@programmerjake](https://internals.rust-lang.org/u/programmerjake)\
**Post date:** [October 14, 2026, 10:38pm UTC](https://internals.rust-lang.org/t/could-we-get-guarantee-that-all-pointer-like-types-have-the-same-representation/24677/3 "2026-10-14T22:38:44Z")

</div>

there's a RFC which would guarantee what you want for slices (edit: apparently it's missing guarantees for `Box<[T]>`):

> <https://github.com/rust-lang/rfcs/pull/3775>
>
> This RFC guarantees the representation of \`&str\` and \`&\[T\]\` to be equivalent to …a \`repr(C)\` struct containing a pointer and a length.
> 
> \[Rendered\](https://github.com/cramertj/rfcs/blob/slice-repr/text/0000-guaranteed-slice-repr.md)

---

<div class="post-metadata">

**Author:** ![the8472](https://avatars.discourse-cdn.com/v4/letter/t/0ea827/32.png) [@the8472](https://internals.rust-lang.org/u/the8472)\
**Post date:** [October 15, 2026, 1:29am UTC](https://internals.rust-lang.org/t/could-we-get-guarantee-that-all-pointer-like-types-have-the-same-representation/24677/4 "2026-10-15T01:29:14Z")

</div>

A reason why they could _hypothetically_ be different is that `&[T]` and `*const [T]` could place different invariants on the length field and thus have different niches, which in turn could lead to different ordering of the `ptr, len` fields.

---

<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:** [October 15, 2026, 2:11am UTC](https://internals.rust-lang.org/t/could-we-get-guarantee-that-all-pointer-like-types-have-the-same-representation/24677/5 "2026-10-15T02:11:55Z")

</div>

Maybe if we don't care. But unlike e.g. the question of the actual layout of pointers to unsized types, I don't see any drawback in guaranteeing it's the same for all pointer-like types, and we can definitely make it happen.

---

<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:** [October 15, 2026, 5:47am UTC](https://internals.rust-lang.org/t/could-we-get-guarantee-that-all-pointer-like-types-have-the-same-representation/24677/6 "2026-10-15T05:47:50Z")

</div>

> [@the8472](#):
>
> that `&[T]` and `*const [T]` could place different invariants on the length field

Note that it's not "could place"; they already have different validity invariants.

(Of course, as you said, that doesn't _have_ to mean that they'd have different layouts.)

---

<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:** [October 15, 2026, 7:06am UTC](https://internals.rust-lang.org/t/could-we-get-guarantee-that-all-pointer-like-types-have-the-same-representation/24677/7 "2026-10-15T07:06:16Z")

</div>

We [guarantee](https://doc.rust-lang.org/nightly/std/primitive.fn.html#abi-compatibility) this:

> `*const T`, `*mut T`, `&T`, `&mut T`, `Box<T>` (specifically, only `Box<T, Global>`), and `NonNull<T>` are all ABI-compatible with each other for all `T`.

Being ABI-compatible only makes sense if they also have the same layout. So we should document that IMO, if we do not already.

However, "same layout" does not imply "same niches" and certainly does not mean that `Wrapper<T>` can be transmuted to `Wrapper<U>` due to associated types and other shenanigans unsafe code could pull. So even if we do make this pointer guarantee, it is not sound to transmute a `HashMap` like you suggest, unless `HashMap` also documents that you can transmute `HashMap<K, V>` to `HashMap<K2, V2>` if the layouts of `K` and `K2`, as well as `V and `V2`, are the same. Currently we don't have any guarantee of that sort to my knowledge.

---

<div class="post-metadata">

**Author:** ![ia0](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ia0/32/1396_2.png) [@ia0](https://internals.rust-lang.org/u/ia0)\
**Post date:** [October 15, 2026, 7:29am UTC](https://internals.rust-lang.org/t/could-we-get-guarantee-that-all-pointer-like-types-have-the-same-representation/24677/8 "2026-10-15T07:29:51Z")

</div>

> [@RalfJung](#):
>
> Currently we don't have any guarantee of that sort to my knowledge

Even for `repr(C)`? Note also that OP is not transmuting `HashMap` but `Foo` which is `repr(C)` and uses its generic as a field only (not some associated type). So it seems the OP example is sound then, using that ABI guarantee to deduce a layout guarantee.

---

<div class="post-metadata">

**Author:** ![Kixunil](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kixunil/32/4110_2.png) [@Kixunil](https://internals.rust-lang.org/u/Kixunil)\
**Post date:** [October 15, 2026, 8:26am UTC](https://internals.rust-lang.org/t/could-we-get-guarantee-that-all-pointer-like-types-have-the-same-representation/24677/9 "2026-10-15T08:26:10Z")

</div>

Thanks! I missed that part.

I'm not however trying to transmute `HashMap`, just a custom `repr(C)` struct designed for this purpose.

Also thanks @programmerjake for the RFC link. Using another depndency has its own significant downsides, so I'd rather avoid that.

---

<div class="post-metadata">

**Author:** ![Kixunil](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kixunil/32/4110_2.png) [@Kixunil](https://internals.rust-lang.org/u/Kixunil)\
**Post date:** [October 15, 2026, 8:39am UTC](https://internals.rust-lang.org/t/could-we-get-guarantee-that-all-pointer-like-types-have-the-same-representation/24677/10 "2026-10-15T08:39:22Z")

</div>

(Actually, I just realized that impl is unsound to expose to safe code but I believe it should be fine in context of `HashMap` if properly encapsulated.)

---

<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:** [October 15, 2026, 8:51am UTC](https://internals.rust-lang.org/t/could-we-get-guarantee-that-all-pointer-like-types-have-the-same-representation/24677/11 "2026-10-15T08:51:24Z")

</div>

> [@ia0](#):
>
> Even for `repr(C)`? Note also that OP is not transmuting `HashMap` but `Foo` which is `repr(C)` and uses its generic as a field only (not some associated type). So it seems the OP example is sound then, using that ABI guarantee to deduce a layout guarantee.

Oh, I misunderstood the text then. I thought it would describe things that happen beyond the code which was shown.

> [@Kixunil](#):
>
> (Actually, I just realized that impl is unsound to expose to safe code but I believe it should be fine in context of `HashMap` if properly encapsulated.)

As far as you know HashMap could do just about anything so if it's unsound to expose to safe code and HashMap does not make specific promises then it's still unsound to expose to HashMap.

---

<div class="post-metadata">

**Author:** ![Kixunil](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kixunil/32/4110_2.png) [@Kixunil](https://internals.rust-lang.org/u/Kixunil)\
**Post date:** [October 16, 2026, 10:51am UTC](https://internals.rust-lang.org/t/could-we-get-guarantee-that-all-pointer-like-types-have-the-same-representation/24677/12 "2026-10-16T10:51:21Z")

</div>

I think it's sound in practice as long as the borrowed type is private because the reason it is unsound is that you can `borrow()` a reference with `'static` lifetime (or in general something longer than what it had) and then cause use-after-free (I tested this) but `HashMap` operates on generic `U: Eq + Hash` where `Key: Borrow<U>` and has no additional knowledge that would allow such operation - it can only compare it and hash it while having it behind the reference coming out of the `Borrow` trait which correctly restricts the lifetime. `Any` is also unusable because the `'static` bound is not there. Perhaps there is a way for it to break with specialization on `Clone` but this can be avoided by making the type intentionally NOT implement `Clone` or any trait other than `Eq`, `PartialEq`, `Hash`, and auto traits (which are markers only, so don't' allow any operations).

So then the responsibility falls on the implementations of `PartialEq` and `Hash` traits on that type which can be carefully designed to not accidentally do something wrong - but really don't have to because just using `#[derive(PartialEq, Hash)]` makes it forward to implementation for `[u8]` which again cannot know that the lifetime appears to be longer, including using specialization tricks.

But if you can find a hole in this reasoning I'm all ears!

---

<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:** [October 16, 2026, 1:13pm UTC](https://internals.rust-lang.org/t/could-we-get-guarantee-that-all-pointer-like-types-have-the-same-representation/24677/13 "2026-10-16T13:13:45Z")

</div>

That is an argument based on parametricity, which is not generally valid reasoning in Rust. There are various ways (type\_id, specialization, try\_as\_dyn) via which HashMap could special-case a type and therefore break the "no additional knowledge" argument.

---

<div class="post-metadata">

**Author:** ![Kixunil](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kixunil/32/4110_2.png) [@Kixunil](https://internals.rust-lang.org/u/Kixunil)\
**Post date:** [October 16, 2026, 1:46pm UTC](https://internals.rust-lang.org/t/could-we-get-guarantee-that-all-pointer-like-types-have-the-same-representation/24677/14 "2026-10-16T13:46:53Z")

</div>

None of those seem to allow seeing the lifetimes and it doesn't appear that any kind of specialization-like feature will ever be built on lifetimes. So I think for any reference contained inside `&'a T` the caller must assume it does NOT live longer than `'a`. The only thing that would allow it is a trait explicitly implemented for something longer but simply not implementing that trait works as long as the type is private.

If there is an actual desire to add a mechanism to inspect the lifetimes of type parameters then I agree that this is problematic.

There's an additional argument: if the offending field is private then anything that accesses it outside of defining module using some specialization-like hacks violates the privacy which is broken at best, unsound at worst.
