11-  Feature Name: ` next_gen_transmute ` 
2- -  Start Date: (fill me in with today's date, YYYY-MM-DD) 
2+ -  Start Date: 2025-08-01 
33-  RFC PR: [ rust-lang/rfcs #0000 ] ( https://github.com/rust-lang/rfcs/pull/0000 ) 
44-  Rust Issue: [ rust-lang/rust #0000 ] ( https://github.com/rust-lang/rust/issues/0000 ) 
55
@@ -31,6 +31,12 @@ to match, and thus there's no opportunity for the compiler to help catch a mista
3131expectation.  Plus it obfuscates other locations that really do want ` transmute_copy ` ,
3232perhaps because they're intentionally reading a prefix out of something.
3333
34+ It's also a safety footgun because it'll * compile*  if you instead were to write
35+ ``` rust 
36+ mem :: transmute_copy (& other )
37+ ``` 
38+ but is highly likely to result in use-after-free UB.
39+ 
3440It would be nice to move ` mem::transmute `  to being a normal function -- not the one
3541intrinsic we let people call directly -- in a way that it can be more flexible for
3642users as well as easier to update in the compiler without semver worries.
@@ -165,7 +171,7 @@ The change to make `mem::transmute` an ordinary function would need to update
165171the existing ` check_transmutes `  in typeck to instead be a lint that looks for
166172calls to ` #[rustc_diagnostic_item = "transmute"] `  instead.  (That diagnostic
167173item already exists.)  For starters the same logic could be used as a
168- deny-be -default lint, as the most similar diagnostics, with any splitting done
174+ deny-by -default lint, as the most similar diagnostics, with any splitting done
169175separately over time at the discretion of the diagnostics experts.
170176
171177This should be straight-forward in CTFE and codegen as well.  Once lowered such
@@ -208,10 +214,15 @@ could be implemented and tested before doing the publicly-visible switchover.
208214
209215Well, there's two big reasons to prefer post-mono here:
210216
211- 1 .  By being post-mono, it's 100% accurate.  Sure, if we could easily be perfectly
212-    accurate earlier in the pipeline that would be nice, but since it's layout-based
213-    that's extremely difficult at best because of layering.  (For anything related
214-    to coroutines that's particularly bad.)
217+ 1 .  By being post-mono, it eliminates all "the compiler isn't smart enough" cases.
218+    If you get an error from it, then the two types are * definitely*  of different
219+    sizes, * period* .  If you find a way to encode Fermat's Last Theorem in the
220+    type system, it's ok, the compiler doesn't have to know how to prove it to let
221+    you do the transmute.  It would be * nice*  if we could be that accurate earlier
222+    in the compilation pipeline, but for anything layout-based that's extremely
223+    difficult -- especially for futures.  There's still the potential for "false"
224+    warnings in code that's only conditionally run, but that's also true of trait
225+    checks, and is thus left for a different RFC to think about.
2152262 .  By being * hard*  errors, rather than lints, there's a bunch more breaking change
216227   concerns.  Any added smarts that allow something to compile need to be around
217228   * forever*  (as removing them would be breaking), and similarly the exact details
@@ -236,18 +247,81 @@ Plus using `union`s for type punning like this is something that people already
236247do, so having a name for it helps make what's happening more obvious, plus gives
237248a place for us to provider better documentation and linting when they do use it.
238249
250+ ## Why the name ` union_transmute ` ?  
251+ 
252+ The idea is to lean on the fact that Rust already has ` union `  as a user-visible
253+ concept, since what this does is * exactly*  the same as using an
254+ all-fields-at-the-same-offset ` union `  to reinterpret the representation.
255+ Similarly, a common way to do this operation in C is to use a union, so people
256+ coming from other languages will recognize it.
257+ 
258+ Thinking about the ` union `  hopefully also give people the right intuition about
259+ the requirements that this has, especially in comparison to what the requirements
260+ would be if this had the pointer-cast semantics.  Hopefully seeing the union in
261+ the name helps them * not*  think that it's just ` (&raw const x).cast().read() ` .
262+ 
263+ There's currently (as an implementation detail) a ` transmute_unchecked `  intrinsic
264+ in rustc which doesn't have the typeck-time size check, but I leaned away from
265+ that name because it's unprecedented, to my knowledge, to have a ` foo_unchecked ` 
266+ in the stable library where ` foo `  is * also*  an ` unsafe fn ` .
267+ 
268+ If we were in a world where ` mem::transmute `  was actually a * safe*  function,
269+ then ` transmute_unchecked `  for this union-semantic version would make sense,
270+ but we don't currently have such a thing.
271+ 
272+ ## Could we keep the compile-time checks on old editions?  
273+ 
274+ This RFC is written assuming that we'll be able to remove the type-checking-time
275+ special behaviour entirely.  That does mean that some things that used to fail
276+ will start to compile, and it's possible that people were writing code depending
277+ on that kind of trickery to enforce invariants.
278+ 
279+ However, there's never been a guarantee about what exactly those checks enforce,
280+ and in general we're always allowed to make previously-no-compiling things start
281+ to compile in new versions -- as has happened before with the check getting
282+ smarter.  We're likely fine saying that such approaches were never endorsed and
283+ thus that libraries should move to other mechanisms to check sizes, as
284+ [ some ecosystem crates] ( https://github.com/Lokathor/bytemuck/pull/320 )  have
285+ already started to do.
286+ 
287+ If for some reason that's not ok, we could consider approaches like
288+ edition-specific name resolution to have ` mem::transmute `  on edition ≤ 2024
289+ continue to get the typeck hacks for this, but on future editions resolve to
290+ the version using the interior const-assert instead.
291+ 
292+ ## Is transmuting to something bigger ever * not*  UB?  
293+ 
294+ As a simple case, if you have
295+ 
296+ ``` rust 
297+ #[repr(C , align(4))]
298+ struct  AlignedByte (u8 );
299+ ``` 
300+ 
301+ then ` union_transmute::<u8, AlignedByte> `  and ` union_transmute::<AlignedByte, u8> ` 
302+ are in fact * both*  always sound, despite the sizes never matching.
303+ 
304+ You can easily make other similar examples using ` repr(packed) `  as well.
305+ 
239306
240307# Prior art  
241308[ prior-art ] : #prior-art 
242309
243- Unknown.
310+ C++ has ` reinterpret_cast `  which sounds like it'd be similar, but which isn't
311+ defined for aggregates, just between integers and pointers or between pointers
312+ and other pointers.
313+ 
314+ GCC has a cast-to-union extension, but it only goes from a value to a ` union ` 
315+ with a field of matching type, and doesn't include the part of going from the
316+ ` union `  back to a different field.
244317
245318
246319# Unresolved questions  
247320[ unresolved-questions ] : #unresolved-questions 
248321
249322During implementation:
250323-  Should MIR's ` CastKind::Transmute `  retain its equal-size precondition?
324+ -  What name should the new function get?
251325
252326For nightly and continuing after stabilization:
253327-  What exactly are the correct lints to have about these functions?
0 commit comments