@@ -210,10 +210,11 @@ macro_rules! transmute {
210210///
211211/// # Size compatibility
212212///
213- /// `transmute_ref!` supports transmuting between `Sized` types or between
214- /// unsized (i.e., `?Sized`) types. It supports any transmutation that preserves
215- /// the number of bytes of the referent, even if doing so requires updating the
216- /// metadata stored in an unsized "fat" reference:
213+ /// `transmute_ref!` supports transmuting between `Sized` types, between unsized
214+ /// (i.e., `?Sized`) types, and from a `Sized` type to an unsized type. It
215+ /// supports any transmutation that preserves the number of bytes of the
216+ /// referent, even if doing so requires updating the metadata stored in an
217+ /// unsized "fat" reference:
217218///
218219/// ```
219220/// # use zerocopy::transmute_ref;
@@ -351,8 +352,28 @@ macro_rules! transmute_ref {
351352 // SAFETY: The `if false` branch ensures that:
352353 // - `Src: IntoBytes + Immutable`
353354 // - `Dst: FromBytes + Immutable`
354- unsafe {
355- t. transmute_ref( )
355+ if false {
356+ // This branch exists solely to force the compiler to infer the
357+ // type of `Dst` *before* it attempts to resolve the method call
358+ // to `transmute_ref` in the `else` branch.
359+ //
360+ // Without this, if `Src` is `Sized` but `Dst` is `!Sized`, the
361+ // compiler will eagerly select the inherent impl of
362+ // `transmute_ref` (which requires `Dst: Sized`) because inherent
363+ // methods take priority over trait methods. It does this before
364+ // it realizes `Dst` is `!Sized`, leading to a compile error when
365+ // it checks the bounds later.
366+ //
367+ // By calling this helper (which returns `&Dst`), we force `Dst`
368+ // to be fully resolved. By the time it gets to the `else`
369+ // branch, the compiler knows `Dst` is `!Sized`, properly
370+ // disqualifies the inherent method, and falls back to the trait
371+ // implementation.
372+ t. transmute_ref_inference_helper( )
373+ } else {
374+ unsafe {
375+ t. transmute_ref( )
376+ }
356377 }
357378 }
358379 } }
@@ -383,10 +404,11 @@ macro_rules! transmute_ref {
383404///
384405/// # Size compatibility
385406///
386- /// `transmute_mut!` supports transmuting between `Sized` types or between
387- /// unsized (i.e., `?Sized`) types. It supports any transmutation that preserves
388- /// the number of bytes of the referent, even if doing so requires updating the
389- /// metadata stored in an unsized "fat" reference:
407+ /// `transmute_mut!` supports transmuting between `Sized` types, between unsized
408+ /// (i.e., `?Sized`) types, and from a `Sized` type to an unsized type. It
409+ /// supports any transmutation that preserves the number of bytes of the
410+ /// referent, even if doing so requires updating the metadata stored in an
411+ /// unsized "fat" reference:
390412///
391413/// ```
392414/// # use zerocopy::transmute_mut;
@@ -503,7 +525,26 @@ macro_rules! transmute_mut {
503525 #[ allow( unused) ]
504526 use $crate:: util:: macro_util:: TransmuteMutDst as _;
505527 let t = $crate:: util:: macro_util:: Wrap :: new( e) ;
506- t. transmute_mut( )
528+ if false {
529+ // This branch exists solely to force the compiler to infer the type
530+ // of `Dst` *before* it attempts to resolve the method call to
531+ // `transmute_mut` in the `else` branch.
532+ //
533+ // Without this, if `Src` is `Sized` but `Dst` is `!Sized`, the
534+ // compiler will eagerly select the inherent impl of `transmute_mut`
535+ // (which requires `Dst: Sized`) because inherent methods take
536+ // priority over trait methods. It does this before it realizes
537+ // `Dst` is `!Sized`, leading to a compile error when it checks the
538+ // bounds later.
539+ //
540+ // By calling this helper (which returns `&mut Dst`), we force `Dst`
541+ // to be fully resolved. By the time it gets to the `else` branch,
542+ // the compiler knows `Dst` is `!Sized`, properly disqualifies the
543+ // inherent method, and falls back to the trait implementation.
544+ t. transmute_mut_inference_helper( )
545+ } else {
546+ t. transmute_mut( )
547+ }
507548 } }
508549}
509550
@@ -1242,6 +1283,13 @@ mod tests {
12421283 const X : & ' static [ [ u8 ; 2 ] ; 4 ] = transmute_ref ! ( & ARRAY_OF_U8S ) ;
12431284 assert_eq ! ( * X , ARRAY_OF_ARRAYS ) ;
12441285
1286+ // Test sized -> unsized transmutation.
1287+ let array_of_u8s = [ 0u8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
1288+ let array_of_arrays = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] , [ 6 , 7 ] ] ;
1289+ let slice_of_arrays = & array_of_arrays[ ..] ;
1290+ let x: & [ [ u8 ; 2 ] ] = transmute_ref ! ( & array_of_u8s) ;
1291+ assert_eq ! ( x, slice_of_arrays) ;
1292+
12451293 // Before 1.61.0, we can't define the `const fn transmute_ref` function
12461294 // that we do on and after 1.61.0.
12471295 #[ cfg( no_zerocopy_generic_bounds_in_const_fn_1_61_0) ]
@@ -1533,6 +1581,13 @@ mod tests {
15331581 let slice_dst_small = SliceDst :: < U16 , u8 > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
15341582 let x: & mut SliceDst < U16 , u8 > = transmute_mut ! ( slice_dst_big) ;
15351583 assert_eq ! ( x, slice_dst_small) ;
1584+
1585+ // Test sized -> unsized transmutation.
1586+ let mut array_of_u8s = [ 0u8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
1587+ let mut array_of_arrays = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] , [ 6 , 7 ] ] ;
1588+ let slice_of_arrays = & mut array_of_arrays[ ..] ;
1589+ let x: & mut [ [ u8 ; 2 ] ] = transmute_mut ! ( & mut array_of_u8s) ;
1590+ assert_eq ! ( x, slice_of_arrays) ;
15361591 }
15371592
15381593 #[ test]
0 commit comments