# Discussion: Editions in Rust-GCC (and other Rust compilers)

**URL:** <https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608>\
**Category:** community\
**Created:** [May 11, 2022, 1:34pm UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608 "2022-05-11T13:34:08Z")\
**Posts on this page:** 20\
**Page:** 4

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [May 24, 2022, 11:49pm UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/61 "2022-05-24T23:49:45Z")

</div>

The point being made is that relying on layout details already is not language UB\[1\]. Making more layout guarantees for `#[repr(Rust)]` makes the hacks more portable between compilers, but it still remains library UB, in that the library author can change the layout and interpretation of the type's private fields without breaking compatibility.

You can claim that private fields could get OSSified into being layout stable, but I don't think making more guarantees on `#[repr(Rust)]` consistency meaningfully changes the calculus here. `#[repr(Rust)]` is already consistent in practice\[2\] (though you aren't _supposed_ to rely on it, just like you're not _supposed_ to rely on being able to transmute your way to private fields) as the PGO field reordering optimization is still purely theoretical and `-Zrandomize-layout` is an opt-in (partly due to not wanting to break code that wrongly assumes `#[repr(Rust)]` is consistent without reason).

* * *

1. It's in the strange place between language UB and just library UB. Relying on layout choices made by the compiler is relying on implementation details, so nonportable (including between compiler versions), but it is not a violation of any rules of the Abstract Machine to read bytes out of a type's representation and then use them at whatever type you want. 

2. Disclaimer: niche availability is also factored into field ordering IIRC, so it's not just based on field size/align. Also don't rely on this until if/when a guarantee is provided, because the flag to break you already exists and could theoretically be turned on by default.

---

<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:** [May 25, 2022, 12:10am UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/62 "2022-05-25T00:10:03Z")

</div>

UB is not a security feature. In practice you can successfully perform operations that the language declares to be UB. In the end it's just a bunch of bytes in your own process and you have full access to all of it.

It would be a bad practice and ugly not-future-proof code, but that's not a security boundary.

---

<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:** [May 25, 2022, 12:15am UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/63 "2022-05-25T00:15:59Z")

</div>

I would very much want `-Zrandomize-layout` to be the default. It should have been the default from Rust 1.0, specifically to avoid future problems with people depending on it. Regardless, with respect to program correctness I have about as much compassion for depending on `#[repr(Rust)]` as for depending on double free and reading freed memory being OK because the allocator should just recycle memory pages for the same process. Which is, absolutely zero compassion. The language should help to enforce correctness and portability, not encourage the can of worms like C++ accidental ABI stabilization.

There is also no good reason why Rust couldn't optimize layouts in a type-specific way regardless of field similarity, even if it doesn't do so today.

> [@CAD97](#):
>
> you aren't _supposed_ to rely on it

It never works this way. Either you enforce that some property cannot be depended upon, or all of your observable behaviour is your public contract. Considering Rust's stability guarantees, this is very disturbing. It's not like we're talking about some weird corner case of unsafe semantics, it's something that is explicitly documented as unspecified and arbitrarily varying behaviour.

Rust goes to great length to enforce the privacy boundaries of libraries and modules, to enforce soundness and the possibility of API evolution. A stable `#[repr(Rust)]` means that subverting all those guarantees is a single google search with a stackoverflow question at the top: "how do I access private fields in Rust". At that point it will be copied everywhere, the most by the people who understand the consequences the least. Why must I even bother with all boilerplate and draconian restrictions of orphan rules, privacy, const correctness and explicit type declarations if I can't rely on it when I need it most?

> [@kornel](#):
>
> UB is not a security feature.

We're not talking about security features, we're talking about which semantics the language guarantees, what counts as backwards compatibility and which way the APIs must be structured. I change a private field and the consumers of my library break. Is it their problem or mine?

---

<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:** [May 25, 2022, 12:23am UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/64 "2022-05-25T00:23:50Z")

</div>

The contract of private fields is that they're private, so whether they're hackable or not is not relevant for the API contract.

---

<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:** [May 25, 2022, 12:30am UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/65 "2022-05-25T00:30:29Z")

</div>

And the contract of C++ ABI is that it is undefined, so it is not relevant whether someone depends on it. Right?

---

<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:** [May 25, 2022, 1:15am UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/66 "2022-05-25T01:15:56Z")

</div>

Please don't be snarky like that.

---

<div class="post-metadata">

**Author:** ![InfernoDeity](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/infernodeity/32/7588_2.png) [@InfernoDeity](https://internals.rust-lang.org/u/InfernoDeity)\
**Post date:** [May 25, 2022, 1:32am UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/67 "2022-05-25T01:32:30Z")

</div>

> [@afetisov](#):
>
> would very much want `-Zrandomize-layout` to be the default. It should have been the default from Rust 1.0, specifically to avoid future problems with people depending on it.

This wastes both memory and execution time. `-Z randomize-layout` is intended as a debugging/fuzzing tool, and is not good for general use.

> [@afetisov](#):
>
> It never works this way. Either you enforce that some property cannot be depended upon, or all of your observable behaviour is your public contract. Considering Rust's stability guarantees, this is very disturbing. It's not like we're talking about some weird corner case of unsafe semantics, it's something that is explicitly documented as unspecified and arbitrarily varying behaviour.

Rust never Promises that it _will_ break `#[repr(Rust)]` layout, only that it's allowed to.

> [@afetisov](#):
>
> A stable `#[repr(Rust)]` means that subverting all those guarantees is a single google search with a stackoverflow question at the top: "how do I access private fields in Rust".

You still don't know what fields might be present in the struct. Private fields can be removed, change, and (as long as at least one private field existed from the start) added. They can also be maintaining arbitrary invariants, and have any meaning whatsoever. This can further change in _any_ update whatsoever. This is true whether it's an unstable `#[repr(Rust)]` under `-Z randomize-layout`, or straight up `#[repr(C)]`, as long as the actual layout of the structure isn't specified by the API of the type.

> [@afetisov](#):
>
> Is it their problem or mine?

Their's.

> [@afetisov](#):
>
> And the contract of C++ ABI is that it is undefined, so it is not relevant whether someone depends on it. Right?

From a standard perspective, yes. Individual implementations do specify a reliable ABI (and bend over backwards to preserve it).

---

<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:** [May 25, 2022, 1:36am UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/68 "2022-05-25T01:36:08Z")

</div>

Here's an actual case of crates depending on a private field in std:

> <https://github.com/rust-lang/rust/pull/78802>
>
> This PR is the result of this internals forum thread: https://internals.rust-lan…g.org/t/why-are-socketaddrv4-socketaddrv6-based-on-low-level-sockaddr-in-6/13321.
> 
> Instead of basing \`std:::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6}\` on system (C) structs, they are encoded in a more optimal and idiomatic Rust way.
> 
> Since this does not change the public API of the standard library in any way I was hoping this did not require an RFC. However, there are non-trivial problems with this PR that must be addressed before it can be merged.
> 
> \### Benefits of this change
> 
> \- It will become possible to move these fundamental network types from \`std\` into \`core\` (\[RFC\](https://github.com/rust-lang/rfcs/pull/2832)).
> \- Many methods that can't be made \`const fn\`s today can be made \`const fn\`s with this change.
> \- \`SocketAddrV4\` only occupies 6 bytes instead of 16 bytes.
> \- These simple primitives become easier to read and uses less \`unsafe\`.
> 
> \### Remaining problems
> 
> This change obviously changes the memory layout of the types. And it turns out some libraries invalidly assumes the memory layout and does very dangerous pointer casts to convert them. These libraries will have undefined behaviour and perform invalid memory access until patched.
> 
> \- \[x\] - \`mio\` - Issue: https://github.com/tokio-rs/mio/issues/1386.
> - \[x\] \`0.7\` branch https://github.com/tokio-rs/mio/pull/1388
> - \[x\] \`0.7.6\` published https://github.com/tokio-rs/mio/pull/1398
> - \[x\] Yank all \`0.7\` versions older than \`0.7.6\`
> - \[x\] Report \`\<0.7.6\` to RustSec Advisory Database https://rustsec.org/advisories/RUSTSEC-2020-0081.html
> \- \[x\] - \`socket2\` - Issue: https://github.com/rust-lang/socket2-rs/issues/119.
> - \[x\] \`0.3.x\` branch https://github.com/rust-lang/socket2-rs/pull/120
> - \[x\] \`0.3.16\` published
> - \[x\] \`master\` branch https://github.com/rust-lang/socket2-rs/pull/122
> - \[x\] Yank all \`0.3\` versions older than \`0.3.16\`
> - \[x\] Report \`\<0.3.16\` to RustSec Advisory Database https://rustsec.org/advisories/RUSTSEC-2020-0079.html
> \- \[x\] - \`net2\` - Issue: https://github.com/deprecrated/net2-rs/issues/105
> - \[x\] https://github.com/deprecrated/net2-rs/pull/106
> - \[x\] \`0.2.36\` published
> - \[x\] Yank all \`0.2\` versions older than \`0.2.36\`
> - \[x\] Report \`\<0.2.36\` to RustSec Advisory Database https://rustsec.org/advisories/RUSTSEC-2020-0078.html
> \- \[x\] - \`miow\` - Issue: https://github.com/yoshuawuyts/miow/issues/38
> - \[x\] \`0.3.x\` - https://github.com/yoshuawuyts/miow/pull/39
> - \[x\] \`0.3.6\` published
> - \[x\] \`0.2.x\` - https://github.com/yoshuawuyts/miow/pull/40
> - \[x\] \`0.2.2\` published
> - \[x\] Yanked all \`0.2\` versions older than \`0.2.2\`
> - \[x\] Yanked all \`0.3\` versions older than \`0.3.6\`
> - \[x\] Report \`\<0.2.2\` and \`\<0.3.6\` to RustSec Advisory Database https://rustsec.org/advisories/RUSTSEC-2020-0080.html
> \- \[x\] - \`quinn master\` (aka what became 0.7) - https://github.com/quinn-rs/quinn/issues/968 https://github.com/quinn-rs/quinn/pull/987
> - \[x\] - \`quinn 0.6\` - https://github.com/quinn-rs/quinn/pull/1045
> - \[x\] - \`quinn 0.5\` - https://github.com/quinn-rs/quinn/pull/1046
> - \[x\] - Release \`0.7.0\`, \`0.6.2\` and \`0.5.4\`
> \- \[x\] - \`nb-connect\` - https://github.com/smol-rs/nb-connect/issues/1
> - \[x\] - Release \`1.0.3\`
> - \[x\] - Yank all versions older than \`1.0.3\`
> \- \[x\] - \`shadowsocks-rust\` - https://github.com/shadowsocks/shadowsocks-rust/issues/462
> \- \[\] - \`rio\` - https://github.com/spacejam/rio/issues/44
> \- \[\] - \`seaslug\` - https://github.com/spacejam/seaslug/issues/1
> 
> \#### Fixed crate versions
> 
> All crates I have found that assumed the memory layout have been fixed and published. The crates and versions that will continue working even as/if this PR is merged is (please upgrade these to help unblock this PR):
> 
> \* \`net2 0.2.36\`
> \* \`socket2 0.3.16\`
> \* \`miow 0.2.2\`
> \* \`miow 0.3.6\`
> \* \`mio 0.7.6\`
> \* \`mio 0.6.23\` - Never had the invalid assumption itself, but has now been bumped to only allow fixed dependencies (\`net2\` + \`miow\`)
> \* \`nb-connect 1.0.3\`
> \* \`quinn 0.5.4\`
> \* \`quinn 0.6.2\`

---

<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:** [May 25, 2022, 1:57am UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/69 "2022-05-25T01:57:41Z")

</div>

> [@InfernoDeity](#):
>
> This wastes both memory and execution time.

That doesn't need to be true. For example, you could pick from one of the possible layout randomizations that doesn't increase the size of the struct from from the minimum. Especially for things like `(Box<T>, bool, bool, bool)` there's, what, a dozen different layouts that are all minimum size?

And I don't think execution time is affected once it's minimum size, because things like cache locality are already not guaranteed because even without _randomization_ you don't know what the order will be.

---

<div class="post-metadata">

**Author:** ![InfernoDeity](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/infernodeity/32/7588_2.png) [@InfernoDeity](https://internals.rust-lang.org/u/InfernoDeity)\
**Post date:** [May 25, 2022, 1:59am UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/70 "2022-05-25T01:59:40Z")

</div>

> [@scottmcm](#):
>
> That doesn't need to be true. For example, you could pick from one of the possible layout randomizations that doesn't increase the size of the struct from from the minimum. Especially for things like `(Box<T>, bool, bool, bool)` there's, what, a dozen different layouts that are all minimum size?

True. I'm thinking of a more aggressive `-Z randomize-layout` that inserts random padding.

---

<div class="post-metadata">

**Author:** ![nacaclanga](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nacaclanga/32/7461_2.png) [@nacaclanga](https://internals.rust-lang.org/u/nacaclanga)\
**Post date:** [May 30, 2022, 9:40am UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/71 "2022-05-30T09:40:39Z")

</div>

Hmm, okay. Personally I don't think one should allow code to mix different stable APIs and hardcode them in a single crate (which would be possible with you notation.) In C++ the implementation of the stable API can be selected with a compiler flag. I believe that's the way to go for Rust (where stable API might not be the `repr(Rust)` but the `repr(Portable)` API). Hence there shouldn't `#[repr(InteroperableRust_2027)]` that can be attached to individual items.

---

<div class="post-metadata">

**Author:** ![InfernoDeity](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/infernodeity/32/7588_2.png) [@InfernoDeity](https://internals.rust-lang.org/u/InfernoDeity)\
**Post date:** [May 30, 2022, 12:51pm UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/72 "2022-05-30T12:51:37Z")

</div>

> [@josh](#):
>
> I do think we should have some very small guarantees about `repr(Rust)` , such as that `(T1, T2, T3)` and `S(T1, T2, T3)` and `S { f1: T1, f2: T2, f3: T3 }` have the same layout and can be transmuted between. But otherwise, yes.

Also, one issue with this: `S(u8, u8, [u32;4])` vs. `S<T: ?Sized>(u8, u8, T)` for `S::<[u32;4]>` (And `(T1, T2, T3)` would have to be treated as the latter).

---

<div class="post-metadata">

**Author:** ![bjorn3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bjorn3/32/2736_2.png) [@bjorn3](https://internals.rust-lang.org/u/bjorn3)\
**Post date:** [May 30, 2022, 2:36pm UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/73 "2022-05-30T14:36:34Z")

</div>

Doesn't `S<T: ?Sized>(u8, u8, T)` only force the `T` to be at the end in memory if `CoerceUnsized` is implemented?

---

<div class="post-metadata">

**Author:** ![steffahn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/steffahn/32/13288_2.png) [@steffahn](https://internals.rust-lang.org/u/steffahn)\
**Post date:** [May 30, 2022, 3:36pm UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/74 "2022-05-30T15:36:24Z")

</div>

`CoerceUnsized` is about coercing pointer types. You can use it on nightly to make your own custom pointer types support unsizing coercion like e.g. `&T` or `Box<T>` do.

The coercion for `S<T: ?Sized>(u8, u8, T)` of something like `S<[u32; 4]>` into `S<[u32]>` is codified by the `Unsize` trait instead, which is _always_ implemented/fulfilled automatically and implicitly by the compiler. E.g. this compiles

```rust
struct S<T: ?Sized>(u8, u8, T);
fn coerce(x: &S<[u32; 4]>) -> &S<[u32]> { x }

```

---

<div class="post-metadata">

**Author:** ![bjorn3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bjorn3/32/2736_2.png) [@bjorn3](https://internals.rust-lang.org/u/bjorn3)\
**Post date:** [May 30, 2022, 4:01pm UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/75 "2022-05-30T16:01:23Z")

</div>

Really? I though you had to opt in to that too.

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [May 30, 2022, 6:38pm UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/76 "2022-05-30T18:38:15Z")

</div>

> [@nacaclanga](#):
>
> Personally I don't think one should allow code to mix different stable APIs and hardcode them in a single crate (which would be possible with you notation.)

It's important to still be able to _consume_ multiple ABIs, so you can use libraries compiled on older ABIs. Otherwise having a stable ABI isn't useful in the first place.

You could argue that splitting the _declaration_ into multiple compilation units (crates) is reasonable, so the ABI is specified once per compilation unit rather than once per item is possible, but requiring it seems unnecessary.

> [@nacaclanga](#):
>
> In C++ the implementation of the stable API can be selected with a compiler flag. I believe that's the way to go for Rust (where stable API might not be the `repr(Rust)` but the `repr(Portable)` API).

In MSVC at least, the calling convention of individual function declarations can be selected with the ` __cdecl`, `__ fastcall`, ` __stdcall`, `__ vectorcall`, and `__thiscall`. The compiler flags `/Gd`, `/Gr`, `/Gv`, and `/Gz` select the default calling convention if unannotated. Being able to annotate declarations is extra important in C++ since headers are textual inclusion and thus need to say which calling convention to expect.

The equivalent for Rust would be `extern "stable-20XX"` to specify a specific calling convention, and compiler/cargo flags to make the default unannotated functions use the specified stable calling convention. (Whether `extern "Rust"` means use the unstable convention or is equivalent to no annotation is a decision to be made if this path is taken.)

A similar case can be made for a `#[repr(Stable20XX)]` layout.

---

<div class="post-metadata">

**Author:** ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)\
**Post date:** [May 30, 2022, 7:06pm UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/77 "2022-05-30T19:06:55Z")

</div>

I really don't think we should have any kind of edition-tied stable ABIs. Rather, I think we should just have a completely separate `extern "safe"` ABI, which is not just edition-independent but _language_-independent, and which supports a subset of Rust functionality. We can expand that over time, and if we ever need to expand it _incompatibly_ we can provide an `extern "safe-2"`.

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [May 30, 2022, 7:35pm UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/78 "2022-05-30T19:35:23Z")

</div>

I actually agree that we don't need an edition-tied calling convention or layout (or the other bits of ABI that I'm sure I'm forgetting).

Calling convention is at least somewhat interesting, since the ideal registers to use may differ between targets, even compatible ones. (See e.g. MSVC x64 ` __fastcall` versus `__ vectorcall`, which enables use of vector registers.)

Layout _without automatic niching_ is basically a solved packing problem (when you have the restriction that size is a multiple of align, anyway) — just stable sort the field order by alignment high to low.

Niching is more complicated, but there's ways of incrementally making niching available in a controlled manner, the first of which would just be "has zero niche" and "uses zero niche" for `Option`.

The ideal would be a truly language independent description of semantic ABI, but we're a long way from making that a reality.

---

<div class="post-metadata">

**Author:** ![nacaclanga](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nacaclanga/32/7461_2.png) [@nacaclanga](https://internals.rust-lang.org/u/nacaclanga)\
**Post date:** [May 31, 2022, 7:50am UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/79 "2022-05-31T07:50:31Z")

</div>

I was more thinking about gcc's `-fabi-version` flag ([C++ Dialect Options (Using the GNU Compiler Collection (GCC))](https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Dialect-Options.html)), when thinking about different ABIs here. ` __cdecl`, `__ fastcall` are like major variant layouts and it makes somewhat sense to use them alongside each other as they all have their pros and cons, whereas I believe the rust-stable ABI, will have a new version mainly because someone found a shortcoming of the current design, just like with new editions.

Of course this will make older ABIs more tricky, but the compiler could still target an older ABI for a specific libary after inspecting it's metadata. The idea would be that a libary will stick to one ABI version until an new major library version is released, which will then upgrade everything to the new version.

Regarding niching, given that we could still rely on precompiled metadata to link to code, even with a stable API, instead of stabilizing the algorithm to lay out struct/enum fields, we could instead use a description of the layout, the compiler came up with when compiling the library. This would be agnostic, or at least backward compatible, over the specific algorithm.

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [May 31, 2022, 1:27pm UTC](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608/80 "2022-05-31T13:27:30Z")

</div>

I think there's two slightly different definitions of "stable ABI" going on here.

The weaker one is just "rustc can link to an rlib compiled by a previous rustc." This doesn't actually require a stable ABI at all, though! What this requires is a forwards compatible rlib format that says which internal version of the Rust ABI is in use, such that new rustc versions can read it and know what ABI to expect from symbols in that library. This would be simplified by using a slower cadence than every six weeks, but the details can be entirely private to rustc and the rlib format so long as other tools aren't expected to injest the information.

The stronger one is "the ABI is predictable and can be linked from other languages." This is required to be able to call Rust from e.g. Zig just from type and function header information and not require the external language tooling to consume the rlib format, or to provide source "import headers" for a binary blob library rather than a full rlib.

While the former isn't truly a _stable_ ABI, it gives much of the benefit of it when just using rustc. It's a useful stepping stone towards a stable ABI, though, and perhaps is all that's really necessary if we expect other tooling to consume it through the rlib descriptor.

Even with a versioned ABI with only minor updates, though, it's still useful to mix ABI versions in a single crate (in C++ you could do so via splitting .cxx files per ABI for your .o which you link as your lib) such that a new version of your library only requires knowledge of the new ABI format for new functions.

[Previous page](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608.md?page=3)

[Next page](https://internals.rust-lang.org/t/discussion-editions-in-rust-gcc-and-other-rust-compilers/16608.md?page=5)
