60
60
//!
61
61
//! - ([`try_`][try_transmute])[`transmute`] (conditionally) converts a value of
62
62
//! one type to a value of another type of the same size
63
- //! - [`transmute_mut`] converts a mutable reference of one type to a mutable
64
- //! reference of another type of the same size
65
- //! - [`transmute_ref`] converts a mutable or immutable reference
66
- //! of one type to an immutable reference of another type of the same size
63
+ //! - ([`try_`][try_transmute_mut])[`transmute_mut`] (conditionally) converts a
64
+ //! mutable reference of one type to a mutable reference of another type of
65
+ //! the same size
66
+ //! - ([`try_`][try_transmute_ref])[`transmute_ref`] (conditionally) converts a
67
+ //! mutable or immutable reference of one type to an immutable reference of
68
+ //! another type of the same size
67
69
//!
68
70
//! These macros perform *compile-time* alignment and size checks, but cannot be
69
71
//! used in generic contexts. For generic conversions, use the methods defined
@@ -4750,7 +4752,7 @@ macro_rules! transmute_mut {
4750
4752
/// This macro behaves like an invocation of this function:
4751
4753
///
4752
4754
/// ```ignore
4753
- /// const fn try_transmute<Src, Dst>(src: Src) -> Result<Dst, ValidityError<Src, Dst>>
4755
+ /// fn try_transmute<Src, Dst>(src: Src) -> Result<Dst, ValidityError<Src, Dst>>
4754
4756
/// where
4755
4757
/// Src: IntoBytes,
4756
4758
/// Dst: TryFromBytes,
@@ -4784,10 +4786,9 @@ macro_rules! transmute_mut {
4784
4786
///
4785
4787
/// // 2u8 → bool = error
4786
4788
/// assert!(matches!(
4787
- /// try_transmute!(3u8 ),
4789
+ /// try_transmute!(2u8 ),
4788
4790
/// Result::<bool, _>::Err(ValidityError { .. })
4789
4791
/// ));
4790
- ///
4791
4792
/// ```
4792
4793
#[ macro_export]
4793
4794
macro_rules! try_transmute {
@@ -4814,6 +4815,221 @@ macro_rules! try_transmute {
4814
4815
} }
4815
4816
}
4816
4817
4818
+ /// Conditionally transmutes a mutable or immutable reference of one type to an
4819
+ /// immutable reference of another type of the same size.
4820
+ ///
4821
+ /// This macro behaves like an invocation of this function:
4822
+ ///
4823
+ /// ```ignore
4824
+ /// fn try_transmute_ref<Src, Dst>(src: &Src) -> Result<&Dst, ValidityError<&Src, &Dst>>
4825
+ /// where
4826
+ /// Src: IntoBytes + Immutable,
4827
+ /// Dst: TryFromBytes + Immutable,
4828
+ /// size_of::<Src>() == size_of::<Dst>(),
4829
+ /// align_of::<Src>() >= align_of::<Dst>(),
4830
+ /// {
4831
+ /// # /*
4832
+ /// ...
4833
+ /// # */
4834
+ /// }
4835
+ /// ```
4836
+ ///
4837
+ /// However, unlike a function, this macro can only be invoked when the types of
4838
+ /// `Src` and `Dst` are completely concrete. The types `Src` and `Dst` are
4839
+ /// inferred from the calling context; they cannot be explicitly specified in
4840
+ /// the macro invocation.
4841
+ ///
4842
+ /// # Examples
4843
+ ///
4844
+ /// ```
4845
+ /// # use zerocopy::*;
4846
+ /// // 0u8 → bool = false
4847
+ /// assert_eq!(try_transmute_ref!(&0u8), Ok(&false));
4848
+ ///
4849
+ /// // 1u8 → bool = true
4850
+ /// assert_eq!(try_transmute_ref!(&1u8), Ok(&true));
4851
+ ///
4852
+ /// // 2u8 → bool = error
4853
+ /// assert!(matches!(
4854
+ /// try_transmute_ref!(&2u8),
4855
+ /// Result::<&bool, _>::Err(ValidityError { .. })
4856
+ /// ));
4857
+ /// ```
4858
+ ///
4859
+ /// # Alignment increase error message
4860
+ ///
4861
+ /// Because of limitations on macros, the error message generated when
4862
+ /// `try_transmute_ref!` is used to transmute from a type of lower alignment to
4863
+ /// a type of higher alignment is somewhat confusing. For example, the following
4864
+ /// code:
4865
+ ///
4866
+ /// ```compile_fail
4867
+ /// let increase_alignment: Result<&u16, _> = zerocopy::try_transmute_ref!(&[0u8; 2]);
4868
+ /// ```
4869
+ ///
4870
+ /// ...generates the following error:
4871
+ ///
4872
+ /// ```text
4873
+ /// error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
4874
+ /// --> example.rs:1:47
4875
+ /// |
4876
+ /// 1 | let increase_alignment: Result<&u16, _> = zerocopy::try_transmute_ref!(&[0u8; 2]);
4877
+ /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4878
+ /// |
4879
+ /// = note: source type: `AlignOf<[u8; 2]>` (8 bits)
4880
+ /// = note: target type: `MaxAlignsOf<[u8; 2], u16>` (16 bits)
4881
+ /// = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `zerocopy::try_transmute_ref` (in Nightly builds, run with -Z macro-backtrace for more info)/// ```
4882
+ /// ```
4883
+ ///
4884
+ /// This is saying that `max(align_of::<T>(), align_of::<U>()) !=
4885
+ /// align_of::<T>()`, which is equivalent to `align_of::<T>() <
4886
+ /// align_of::<U>()`.
4887
+ #[ macro_export]
4888
+ macro_rules! try_transmute_ref {
4889
+ ( $e: expr) => { {
4890
+ // NOTE: This must be a macro (rather than a function with trait bounds)
4891
+ // because there's no way, in a generic context, to enforce that two
4892
+ // types have the same size. `core::mem::transmute` uses compiler magic
4893
+ // to enforce this so long as the types are concrete.
4894
+
4895
+ // Ensure that the source type is a reference or a mutable reference
4896
+ // (note that mutable references are implicitly reborrowed here).
4897
+ let e: & _ = $e;
4898
+
4899
+ #[ allow( unreachable_code, unused, clippy:: diverging_sub_expression) ]
4900
+ if false {
4901
+ // This branch, though never taken, ensures that `size_of::<T>() ==
4902
+ // size_of::<U>()` and that that `align_of::<T>() >=
4903
+ // align_of::<U>()`.
4904
+
4905
+ // `t` is inferred to have type `T` because it's assigned to `e` (of
4906
+ // type `&T`) as `&t`.
4907
+ let mut t = loop { } ;
4908
+ e = & t;
4909
+
4910
+ // `u` is inferred to have type `U` because it's used as `Ok(&u)` as
4911
+ // the value returned from this branch.
4912
+ let u;
4913
+
4914
+ $crate:: assert_size_eq!( t, u) ;
4915
+ $crate:: assert_align_gt_eq!( t, u) ;
4916
+
4917
+ Ok ( & u)
4918
+ } else {
4919
+ $crate:: macro_util:: try_transmute_ref:: <_, _>( e)
4920
+ }
4921
+ } }
4922
+ }
4923
+
4924
+ /// Conditionally transmutes a mutable reference of one type to a mutable
4925
+ /// reference of another type of the same size.
4926
+ ///
4927
+ /// This macro behaves like an invocation of this function:
4928
+ ///
4929
+ /// ```ignore
4930
+ /// fn try_transmute_mut<Src, Dst>(src: &mut Src) -> Result<&mut Dst, ValidityError<&mut Src, &mut Dst>>
4931
+ /// where
4932
+ /// Src: IntoBytes,
4933
+ /// Dst: TryFromBytes,
4934
+ /// size_of::<Src>() == size_of::<Dst>(),
4935
+ /// align_of::<Src>() >= align_of::<Dst>(),
4936
+ /// {
4937
+ /// # /*
4938
+ /// ...
4939
+ /// # */
4940
+ /// }
4941
+ /// ```
4942
+ ///
4943
+ /// However, unlike a function, this macro can only be invoked when the types of
4944
+ /// `Src` and `Dst` are completely concrete. The types `Src` and `Dst` are
4945
+ /// inferred from the calling context; they cannot be explicitly specified in
4946
+ /// the macro invocation.
4947
+ ///
4948
+ /// # Examples
4949
+ ///
4950
+ /// ```
4951
+ /// # use zerocopy::*;
4952
+ /// // 0u8 → bool = false
4953
+ /// let src = &mut 0u8;
4954
+ /// assert_eq!(try_transmute_mut!(src), Ok(&mut false));
4955
+ ///
4956
+ /// // 1u8 → bool = true
4957
+ /// let src = &mut 1u8;
4958
+ /// assert_eq!(try_transmute_mut!(src), Ok(&mut true));
4959
+ ///
4960
+ /// // 2u8 → bool = error
4961
+ /// let src = &mut 2u8;
4962
+ /// assert!(matches!(
4963
+ /// try_transmute_mut!(src),
4964
+ /// Result::<&mut bool, _>::Err(ValidityError { .. })
4965
+ /// ));
4966
+ /// ```
4967
+ ///
4968
+ /// # Alignment increase error message
4969
+ ///
4970
+ /// Because of limitations on macros, the error message generated when
4971
+ /// `try_transmute_ref!` is used to transmute from a type of lower alignment to
4972
+ /// a type of higher alignment is somewhat confusing. For example, the following
4973
+ /// code:
4974
+ ///
4975
+ /// ```compile_fail
4976
+ /// let src = &mut [0u8; 2];
4977
+ /// let increase_alignment: Result<&mut u16, _> = zerocopy::try_transmute_mut!(src);
4978
+ /// ```
4979
+ ///
4980
+ /// ...generates the following error:
4981
+ ///
4982
+ /// ```text
4983
+ /// error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
4984
+ /// --> example.rs:2:51
4985
+ /// |
4986
+ /// 2 | let increase_alignment: Result<&mut u16, _> = zerocopy::try_transmute_mut!(src);
4987
+ /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4988
+ /// |
4989
+ /// = note: source type: `AlignOf<[u8; 2]>` (8 bits)
4990
+ /// = note: target type: `MaxAlignsOf<[u8; 2], u16>` (16 bits)
4991
+ /// = note: this error originates in the macro `$crate::assert_align_gt_eq` which comes from the expansion of the macro `zerocopy::try_transmute_mut` (in Nightly builds, run with -Z macro-backtrace for more info)
4992
+ /// ```
4993
+ ///
4994
+ /// This is saying that `max(align_of::<T>(), align_of::<U>()) !=
4995
+ /// align_of::<T>()`, which is equivalent to `align_of::<T>() <
4996
+ /// align_of::<U>()`.
4997
+ #[ macro_export]
4998
+ macro_rules! try_transmute_mut {
4999
+ ( $e: expr) => { {
5000
+ // NOTE: This must be a macro (rather than a function with trait bounds)
5001
+ // because there's no way, in a generic context, to enforce that two
5002
+ // types have the same size. `core::mem::transmute` uses compiler magic
5003
+ // to enforce this so long as the types are concrete.
5004
+
5005
+ // Ensure that the source type is a mutable reference.
5006
+ let e: & mut _ = $e;
5007
+
5008
+ #[ allow( unreachable_code, unused, clippy:: diverging_sub_expression) ]
5009
+ if false {
5010
+ // This branch, though never taken, ensures that `size_of::<T>() ==
5011
+ // size_of::<U>()` and that that `align_of::<T>() >=
5012
+ // align_of::<U>()`.
5013
+
5014
+ // `t` is inferred to have type `T` because it's assigned to `e` (of
5015
+ // type `&mut T`) as `&mut t`.
5016
+ let mut t = loop { } ;
5017
+ e = & mut t;
5018
+
5019
+ // `u` is inferred to have type `U` because it's used as `Ok(&mut
5020
+ // u)` as the value returned from this branch.
5021
+ let u;
5022
+
5023
+ $crate:: assert_size_eq!( t, u) ;
5024
+ $crate:: assert_align_gt_eq!( t, u) ;
5025
+
5026
+ Ok ( & mut u)
5027
+ } else {
5028
+ $crate:: macro_util:: try_transmute_mut:: <_, _>( e)
5029
+ }
5030
+ } }
5031
+ }
5032
+
4817
5033
/// Includes a file and safely transmutes it to a value of an arbitrary type.
4818
5034
///
4819
5035
/// The file will be included as a byte array, `[u8; N]`, which will be
@@ -5775,6 +5991,71 @@ mod tests {
5775
5991
mem:: forget ( y) ;
5776
5992
}
5777
5993
5994
+ #[ test]
5995
+ fn test_try_transmute_ref ( ) {
5996
+ // Test that memory is transmuted with `try_transmute_ref` as expected.
5997
+ let array_of_bools = & [ false , true , false , true , false , true , false , true ] ;
5998
+ let array_of_arrays = & [ [ 0 , 1 ] , [ 0 , 1 ] , [ 0 , 1 ] , [ 0 , 1 ] ] ;
5999
+ let x: Result < & [ [ u8 ; 2 ] ; 4 ] , _ > = try_transmute_ref ! ( array_of_bools) ;
6000
+ assert_eq ! ( x, Ok ( array_of_arrays) ) ;
6001
+ let x: Result < & [ bool ; 8 ] , _ > = try_transmute_ref ! ( array_of_arrays) ;
6002
+ assert_eq ! ( x, Ok ( array_of_bools) ) ;
6003
+
6004
+ // Test that it's legal to transmute a reference while shrinking the
6005
+ // lifetime.
6006
+ {
6007
+ let x: Result < & [ [ u8 ; 2 ] ; 4 ] , _ > = try_transmute_ref ! ( array_of_bools) ;
6008
+ assert_eq ! ( x, Ok ( array_of_arrays) ) ;
6009
+ }
6010
+
6011
+ // Test that `try_transmute_ref!` supports decreasing alignment.
6012
+ let u = AU64 ( 0 ) ;
6013
+ let array = [ 0u8 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ;
6014
+ let x: Result < & [ u8 ; 8 ] , _ > = try_transmute_ref ! ( & u) ;
6015
+ assert_eq ! ( x, Ok ( & array) ) ;
6016
+
6017
+ // Test that a mutable reference can be turned into an immutable one.
6018
+ let mut x = 0u8 ;
6019
+ #[ allow( clippy:: useless_transmute) ]
6020
+ let y: Result < & u8 , _ > = try_transmute_ref ! ( & mut x) ;
6021
+ assert_eq ! ( y, Ok ( & 0 ) ) ;
6022
+ }
6023
+
6024
+ #[ test]
6025
+ fn test_try_transmute_mut ( ) {
6026
+ // Test that memory is transmuted with `try_transmute_mut` as expected.
6027
+ let array_of_bools = & mut [ false , true , false , true , false , true , false , true ] ;
6028
+ let array_of_arrays = & mut [ [ 0u8 , 1 ] , [ 0 , 1 ] , [ 0 , 1 ] , [ 0 , 1 ] ] ;
6029
+ let x: Result < & mut [ [ u8 ; 2 ] ; 4 ] , _ > = try_transmute_mut ! ( array_of_bools) ;
6030
+ assert_eq ! ( x, Ok ( array_of_arrays) ) ;
6031
+
6032
+ let array_of_bools = & mut [ false , true , false , true , false , true , false , true ] ;
6033
+ let array_of_arrays = & mut [ [ 0u8 , 1 ] , [ 0 , 1 ] , [ 0 , 1 ] , [ 0 , 1 ] ] ;
6034
+ let x: Result < & mut [ bool ; 8 ] , _ > = try_transmute_mut ! ( array_of_arrays) ;
6035
+ assert_eq ! ( x, Ok ( array_of_bools) ) ;
6036
+
6037
+ // Test that it's legal to transmute a reference while shrinking the
6038
+ // lifetime.
6039
+ let array_of_bools = & mut [ false , true , false , true , false , true , false , true ] ;
6040
+ let array_of_arrays = & mut [ [ 0u8 , 1 ] , [ 0 , 1 ] , [ 0 , 1 ] , [ 0 , 1 ] ] ;
6041
+ {
6042
+ let x: Result < & mut [ [ u8 ; 2 ] ; 4 ] , _ > = try_transmute_mut ! ( array_of_bools) ;
6043
+ assert_eq ! ( x, Ok ( array_of_arrays) ) ;
6044
+ }
6045
+
6046
+ // Test that `try_transmute_mut!` supports decreasing alignment.
6047
+ let u = & mut AU64 ( 0 ) ;
6048
+ let array = & mut [ 0u8 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ;
6049
+ let x: Result < & mut [ u8 ; 8 ] , _ > = try_transmute_mut ! ( u) ;
6050
+ assert_eq ! ( x, Ok ( array) ) ;
6051
+
6052
+ // Test that a mutable reference can be turned into an immutable one.
6053
+ let mut x = 0u8 ;
6054
+ #[ allow( clippy:: useless_transmute) ]
6055
+ let y: Result < & mut u8 , _ > = try_transmute_mut ! ( & mut x) ;
6056
+ assert_eq ! ( y, Ok ( & mut 0 ) ) ;
6057
+ }
6058
+
5778
6059
#[ test]
5779
6060
fn test_transmute_mut ( ) {
5780
6061
// Test that memory is transmuted as expected.
0 commit comments