Repository: Lokathor/bytemuck
Pull request #58 (2021-03-21 21:13:46)

[Feature] extend `TransparentWrapper` conversion functions
This PR extends the `TransparentWrapper` trait by adding new conversion functions.
Namely:

These three missing `wrap_{}` functions.
- `TransparentWrapper::wrap(s: Inner) -> Self`
- `TransparentWrapper::wrap_slice(s: &[Inner]) -> &[Self]`
- `TransparentWrapper::wrap_slice_mut(s: &mut [Inner]) -> &mut [Self]`

These five `unwrap_{}` functions, which are the counterparts of the `wrap_{}` functions. They just invert the conversion.
- `TransparentWrapper::unwrap(self) -> Inner`
- `TransparentWrapper::unwrap_ref(&self) -> &Inner`
- `TransparentWrapper::unwrap_mut(&mut self) -> &mut Inner`
- `TransparentWrapper::unwrap_slice(s: &[Self]) -> &[Inner]`
- `TransparentWrapper::unwrap_slice_mut(s: &mut [Self]) -> &mut [Inner]`


This is useful for implementing `Zeroable` and `Pod` on types from extern crates in order to use `bytemuck::{}` functions like `bytemuck::cast_slice` using the new-ype pattern and `TransparentWrapper`.

The goal of this PR is to support all `bytemuck::{}` functions.

This is possible with this PR:
```rust
  // An external type defined in a different crate.
  #[derive(Copy, Clone, Default)]
  struct Foreign(u8);

  use bytemuck::TransparentWrapper;

  #[derive(Copy, Clone)]
  #[repr(transparent)]
  struct Wrapper(Foreign);

  unsafe impl TransparentWrapper<Foreign> for Wrapper {}

  // Traits can be implemented on crate-local wrapper.
  unsafe impl bytemuck::Zeroable for Wrapper {}
  unsafe impl bytemuck::Pod for Wrapper {}

  let _: u8 = bytemuck::cast(Wrapper::wrap(Foreign::default()));
  let _: Foreign = Wrapper::unwrap(bytemuck::cast(u8::default()));

  let _: &u8 = bytemuck::cast_ref(Wrapper::wrap_ref(&Foreign::default()));
  let _: &Foreign = Wrapper::unwrap_ref(bytemuck::cast_ref(&u8::default()));

  let _: &mut u8 = bytemuck::cast_mut(Wrapper::wrap_mut(&mut Foreign::default()));
  let _: &mut Foreign = Wrapper::unwrap_mut(bytemuck::cast_mut(&mut u8::default()));

  let _: &[u8] = bytemuck::cast_slice(Wrapper::wrap_slice(&[Foreign::default()]));
  let _: &[Foreign] = Wrapper::unwrap_slice(bytemuck::cast_slice(&[u8::default()]));

  let _: &mut [u8] = bytemuck::cast_slice_mut(Wrapper::wrap_slice_mut(&mut [Foreign::default()]));
  let _: &mut [Foreign] = Wrapper::unwrap_slice_mut(bytemuck::cast_slice_mut(&mut [u8::default()]));

  let _: &[u8] = bytemuck::bytes_of(Wrapper::wrap_ref(&Foreign::default()));
  let _: &Foreign = Wrapper::unwrap_ref(bytemuck::from_bytes(&[u8::default()]));

  let _: &mut [u8] = bytemuck::bytes_of_mut(Wrapper::wrap_mut(&mut Foreign::default()));
  let _: &mut Foreign = Wrapper::unwrap_mut(bytemuck::from_bytes_mut(&mut [u8::default()]));

  // not sure if this is the right usage
  let _ = bytemuck::pod_align_to::<_, u8>(Wrapper::wrap_slice(&[Foreign::default()]));
  // counterpart?

  // not sure if this is the right usage
  let _ = bytemuck::pod_align_to_mut::<_, u8>(Wrapper::wrap_slice_mut(&mut [Foreign::default()]));
  // counterpart?
```

This PR also includes a test, which uses the exact example code from above. This is for MIRI to check for any potential UB.
As you can see, there currently isn't any checking done on the output of the functions. This is something left todo.

Can somebody please check the soundness of the implementation, because I have very little expertise with unsafe code.

I've written up some docs on how this trait can be used in combination with the newtype pattern to use the bytemuck functions on foreign types from external crates.

I also adjusted `rustfmt.toml` in the first commit, because of the deprecation of `merge_imports = true`. It is being replaced by `imports_granularity = "Crate"`. Furthermore I ran `cargo +nightly fmt` to format the codebase. I hope these two changes are fine.

I'm open to suggestions!

This is my first PR ever 🥳 .

Lokathor: This looks correct.

I'll have @thomcc take a look too, because they wrote this trait.
LU15W1R7H: I've now also added the mutable counterpart (`wrap_slice_mut`) to `wrap_slice`.

Instead of mentioning all 4 methods in the safety contract doc, I changed it to say "All functions on `TransparentWrapper` may not be overwritten."
LU15W1R7H: I've decided to extend this PR some more, therefore I converted it into a draft.
I would like to add all conversions needed, to support all bytemuck functions.
LU15W1R7H: I've now rewritten and extended the PR quite a bit. Please take a look at it again :).

I would also like to introduce the `wrap` and `unwrap` conversions functions.
This would be nice, because than the conversion for `bytemuck::cast` would look the same as all other conversions.
We could probably just do a transmute.
This would require a `Sized` bound on the function because we need to return a statically sized type.

Is this desirable?
Lokathor: I believe that the transmute_copy trick used to wrap can also be used to unwrap.
LU15W1R7H: Okay, another rather big overhaul of this PR is done 😄 .

I adjusted the docs a little to be more concise. Also I renamed `Wrapped` to `Inner` to make it visually more distinct from `Wrapper`.

I've also added the `wrap` and `unwrap` functions. I didn't use the `transmute_copy` **trick** because I don't have to transmute any pointers since there is a `Sized` bound on `Self` and `Inner`. This is needed because rust needs `Sized` functions arguments and return types.

I also added a test which currently just checks for UB using MIRI. Thanks to this I already catched a bug causing UB.
There isn't any actual checking done on the results of the functions.
thomcc: I'm not sure `unwrap` is the right term for these, given that that terminology is already used very commonly in Rust to mean something different...
LU15W1R7H: @thomcc I agree, but I haven't come up with a better fitting terminology. It is a wrapper after all :D.
I'm open for suggestions.
LU15W1R7H: I've now added some docs and a example mentioning how `TransparentWrapper` can be used to implement traits like `Zeroable` and `Pod` on external types.
LU15W1R7H: This PR is no longer a draft! I'm pretty happy with it.

Now I'm waiting for a review and some feedback on what's left to do :).
Lokathor: I feel like the derive should continue to fail even then. You can still write it manually. The fact that you didn't just derive it is a good time to say "i know the base type doesn't impl the trait but i trust i can bend the rule in this case"
LU15W1R7H: @Lokathor Hmm, It's just unfortunate, that in this case you can't have the derive check, if the safety guarantees hold.

Does the current proc-macro implementation check all aspects of the unsafe contract? It should, because otherwise there would be a possibility of introducing UB without having an unsafe block...

If the derive can check all aspects of the unsafe contract, then you shouldn't need to think about bending the rules, because it's guaranteed to be safe and sound :).
I'm just not sure if these new restrictions are easily checkable.


Lokathor: It may be possible to fix. We could probably finish off the PR noe and file an issue for the derives later.
LU15W1R7H: Sounds good to me!
thomcc: I think I'm not really a fan of having this many functions on the trait. It would probably be better to move some/most of them to free functions, since at this point there's a real risk of collision with inherent or deref impls, especially with the generic naming scheme...
LU15W1R7H: @thomcc
I'm not quite sure how collisions might happen?
Is it enough to change the `self` to `Self` in the arguments of `unwrap`, `unwrap_ref` and `unwrap_mut`?
LU15W1R7H: Anything left to do?
Lokathor: I haven't had time to get to this. My "weekend" is Sun/Mon, so I'll try to get this merged.

Patch:
diff --git a/rustfmt.toml b/rustfmt.toml
index f214385..689ceb1 100644
--- a/rustfmt.toml
+++ b/rustfmt.toml
@@ -12,5 +12,5 @@ use_small_heuristics = "Max"
 
 # Unstable
 format_code_in_doc_comments = true
-merge_imports = true
+imports_granularity = "Crate"
 wrap_comments = true
diff --git a/src/contiguous.rs b/src/contiguous.rs
index 30709a7..b60f4a4 100644
--- a/src/contiguous.rs
+++ b/src/contiguous.rs
@@ -85,8 +85,8 @@ pub unsafe trait Contiguous: Copy + 'static {
   /// `#[repr(Int)]` or `#[repr(C)]` attribute, (if it does not, it is
   /// *unsound* to implement `Contiguous`!).
   ///
-  /// - For `#[repr(Int)]`, use the listed `Int`. e.g. `#[repr(u8)]` should
-  ///   use `type Int = u8`.
+  /// - For `#[repr(Int)]`, use the listed `Int`. e.g. `#[repr(u8)]` should use
+  ///   `type Int = u8`.
   ///
   /// - For `#[repr(C)]`, use whichever type the C compiler will use to
   ///   represent the given enum. This is usually `c_int` (from `std::os::raw`
diff --git a/src/offset_of.rs b/src/offset_of.rs
index 604379a..7e8aedf 100644
--- a/src/offset_of.rs
+++ b/src/offset_of.rs
@@ -25,7 +25,9 @@
 /// # use bytemuck::offset_of;
 /// // enums can't derive default, and for this example we don't pick one
 /// enum MyExampleEnum {
-///   A, B, C,
+///   A,
+///   B,
+///   C,
 /// }
 ///
 /// // so now our struct here doesn't have Default
@@ -65,11 +67,11 @@
 /// [rust-lang/rust#27060]: https://github.com/rust-lang/rust/issues/27060
 ///
 /// <p style="background:rgba(255,181,77,0.16);padding:0.75em;">
-/// <strong>Warning:</strong> This is only true for versions of bytemuck > 1.4.0.
-/// Previous versions of
+/// <strong>Warning:</strong> This is only true for versions of bytemuck >
+/// 1.4.0. Previous versions of
 /// <code style="background:rgba(41,24,0,0.1);">bytemuck::offset_of!</code>
-/// will only emit a warning when used on the field of a packed struct in safe code,
-/// which can lead to unsoundness.
+/// will only emit a warning when used on the field of a packed struct in safe
+/// code, which can lead to unsoundness.
 /// </p>
 ///
 /// For example, the following will fail to compile:
@@ -91,7 +93,8 @@
 /// ```compile_fail
 /// # #[repr(C, packed)] #[derive(Default)] struct Example { field: u32 }
 /// // Still doesn't compile:
-/// #[allow(safe_packed_borrows)] {
+/// #[allow(safe_packed_borrows)]
+/// {
 ///   let _offset = bytemuck::offset_of!(Example, field);
 /// }
 /// ```
diff --git a/src/transparent.rs b/src/transparent.rs
index fa6d479..6fb53bc 100644
--- a/src/transparent.rs
+++ b/src/transparent.rs
@@ -1,38 +1,42 @@
 use super::*;
 
-/// A trait which indicates that a type is a `repr(transparent)` wrapper around
-/// the `Wrapped` value.
+/// A trait which indicates that a type is a `#[repr(transparent)]` wrapper
+/// around the `Inner` value.
 ///
-/// This allows safely creating references to `T` from those to the `Wrapped`
-/// type, using the `wrap_ref` and `wrap_mut` functions.
+/// This allows safely copy transmuting between the `Inner` type and the
+/// `TransparentWrapper` type.  
+/// Functions like `wrap_{}` convert from the inner
+/// type to the wrapper type and `unwrap_{}` functions do the inverse conversion
+/// from the wrapper type to the inner type.
 ///
 /// # Safety
 ///
 /// The safety contract of `TransparentWrapper` is relatively simple:
 ///
-/// For a given `Wrapper` which implements `TransparentWrapper<Wrapped>`:
+/// For a given `Wrapper` which implements `TransparentWrapper<Inner>`:
 ///
-/// 1. Wrapper must be a `#[repr(transparent)]` wrapper around `Wrapped`. This
-///    either means that it must be a `#[repr(transparent)]` struct which
-///    contains a either a field of type `Wrapped` (or a field of some other
-///    transparent wrapper for `Wrapped`) as the only non-ZST field.
+/// 1. `Wrapper` must be a wrapper around `Inner` with an identical data
+/// representations. This    either means that it must be a
+/// `#[repr(transparent)]` struct which    contains a either a field of type
+/// `Inner` (or a field of some other    transparent wrapper for `Inner`) as the
+/// only non-ZST field.
 ///
-/// 2. Any fields *other* than the `Wrapped` field must be trivially
+/// 2. Any fields *other* than the `Inner` field must be trivially
 ///    constructable ZSTs, for example `PhantomData`, `PhantomPinned`, etc.
 ///
 /// 3. The `Wrapper` may not impose additional alignment requirements over
-///    `Wrapped`.
+///    `Inner`.
 ///     - Note: this is currently guaranteed by `repr(transparent)`, but there
 ///       have been discussions of lifting it, so it's stated here explicitly.
 ///
-/// 4. The `wrap_ref` and `wrap_mut` functions on `TransparentWrapper` may not
-///    be overridden.
+/// 4. All functions on
+///    `TransparentWrapper` **may not** be overridden.
 ///
 /// ## Caveats
 ///
-/// If the wrapper imposes additional constraints upon the wrapped type which
+/// If the wrapper imposes additional constraints upon the inner type which
 /// are required for safety, it's responsible for ensuring those still hold --
-/// this generally requires preventing access to instances of the wrapped type,
+/// this generally requires preventing access to instances of the inner type,
 /// as implementing `TransparentWrapper<U> for T` means anybody can call
 /// `T::cast_ref(any_instance_of_u)`.
 ///
@@ -55,13 +59,13 @@ use super::*;
 ///
 /// // interpret a reference to &SomeStruct as a &MyWrapper
 /// let thing = SomeStruct::default();
-/// let wrapped_ref: &MyWrapper = MyWrapper::wrap_ref(&thing);
+/// let inner_ref: &MyWrapper = MyWrapper::wrap_ref(&thing);
 ///
 /// // Works with &mut too.
 /// let mut mut_thing = SomeStruct::default();
-/// let wrapped_mut: &mut MyWrapper = MyWrapper::wrap_mut(&mut mut_thing);
+/// let inner_mut: &mut MyWrapper = MyWrapper::wrap_mut(&mut mut_thing);
 ///
-/// # let _ = (wrapped_ref, wrapped_mut); // silence warnings
+/// # let _ = (inner_ref, inner_mut); // silence warnings
 /// ```
 ///
 /// ## Use with dynamically sized types
@@ -80,56 +84,166 @@ use super::*;
 /// let mut buf = [1, 2, 3u8];
 /// let sm = Slice::wrap_mut(&mut buf);
 /// ```
-pub unsafe trait TransparentWrapper<Wrapped: ?Sized> {
-  /// Convert a reference to a wrapped type into a reference to the wrapper.
-  ///
-  /// This is a trait method so that you can write `MyType::wrap_ref(...)` in
-  /// your code. It is part of the safety contract for this trait that if you
-  /// implement `TransparentWrapper<_>` for your type you **must not** override
-  /// this method.
+pub unsafe trait TransparentWrapper<Inner: ?Sized> {
+  /// Convert the inner type into the wrapper type.
   #[inline]
-  fn wrap_ref(s: &Wrapped) -> &Self {
+  fn wrap(s: Inner) -> Self
+  where
+    Self: Sized,
+    Inner: Sized,
+  {
+    // SAFETY: The unsafe contract requires that `Self` and `Inner` have
+    // identical representations.
+    unsafe { transmute_copy(&s) }
+  }
+
+  /// Convert a reference to the inner type into a reference to the wrapper
+  /// type.
+  #[inline]
+  fn wrap_ref(s: &Inner) -> &Self {
     unsafe {
-      assert!(size_of::<*const Wrapped>() == size_of::<*const Self>());
-      // Using a pointer cast doesn't work here because rustc can't tell that the
-      // vtables match (if we lifted the ?Sized restriction, this would go away),
-      // and transmute doesn't work for the usual reasons it doesn't work inside
-      // generic functions.
+      assert!(size_of::<*const Inner>() == size_of::<*const Self>());
+      // A pointer cast does't work here because rustc can't tell that
+      // the vtables match (because of the `?Sized` restriction relaxation).
+      // A `transmute` doesn't work because the sizes are unspecified.
       //
-      // SAFETY: The unsafe contract requires that these have identical
-      // representations. Using this transmute_copy instead of transmute here is
-      // annoying, but is required as `Self` and `Wrapped` have unspecified
-      // sizes still.
-      let wrapped_ptr = s as *const Wrapped;
-      let wrapper_ptr: *const Self = transmute_copy(&wrapped_ptr);
+      // SAFETY: The unsafe contract requires that these two have
+      // identical representations.
+      let inner_ptr = s as *const Inner;
+      let wrapper_ptr: *const Self = transmute_copy(&inner_ptr);
       &*wrapper_ptr
     }
   }
 
-  /// Convert a mut reference to a wrapped type into a mut reference to the
-  /// wrapper.
-  ///
-  /// This is a trait method so that you can write `MyType::wrap_mut(...)` in
-  /// your code. It is part of the safety contract for this trait that if you implement
-  /// `TransparentWrapper<_>` for your type you **must not** override this method.
+  /// Convert a mutable reference to the inner type into a mutable reference to
+  /// the wrapper type.
   #[inline]
-  fn wrap_mut(s: &mut Wrapped) -> &mut Self {
+  fn wrap_mut(s: &mut Inner) -> &mut Self {
     unsafe {
-      assert!(size_of::<*mut Wrapped>() == size_of::<*mut Self>());
-      // Using a pointer cast doesn't work here because rustc can't tell that the
-      // vtables match (if we lifted the ?Sized restriction, this would go away),
-      // and transmute doesn't work for the usual reasons it doesn't work inside
-      // generic functions.
+      assert!(size_of::<*mut Inner>() == size_of::<*mut Self>());
+      // A pointer cast does't work here because rustc can't tell that
+      // the vtables match (because of the `?Sized` restriction relaxation).
+      // A `transmute` doesn't work because the sizes are unspecified.
       //
-      // SAFETY: The unsafe contract requires that these have identical
-      // representations. Using this transmute_copy instead of transmute here is
-      // annoying, but is required as `Self` and `Wrapped` have unspecified
-      // sizes still.
-      let wrapped_ptr = s as *mut Wrapped;
-      let wrapper_ptr: *mut Self = transmute_copy(&wrapped_ptr);
+      // SAFETY: The unsafe contract requires that these two have
+      // identical representations.
+      let inner_ptr = s as *mut Inner;
+      let wrapper_ptr: *mut Self = transmute_copy(&inner_ptr);
       &mut *wrapper_ptr
     }
   }
+
+  /// Convert a slice to the inner type into a slice to the wrapper type.
+  #[inline]
+  fn wrap_slice(s: &[Inner]) -> &[Self]
+  where
+    Self: Sized,
+    Inner: Sized,
+  {
+    unsafe {
+      assert!(size_of::<*const Inner>() == size_of::<*const Self>());
+      assert!(align_of::<*const Inner>() == align_of::<*const Self>());
+      // SAFETY: The unsafe contract requires that these two have
+      // identical representations (size and alignment).
+      core::slice::from_raw_parts(s.as_ptr() as *const Self, s.len())
+    }
+  }
+
+  /// Convert a mutable slice to the inner type into a mutable slice to the
+  /// wrapper type.
+  #[inline]
+  fn wrap_slice_mut(s: &mut [Inner]) -> &mut [Self]
+  where
+    Self: Sized,
+    Inner: Sized,
+  {
+    unsafe {
+      assert!(size_of::<*mut Inner>() == size_of::<*mut Self>());
+      assert!(align_of::<*mut Inner>() == align_of::<*mut Self>());
+      // SAFETY: The unsafe contract requires that these two have
+      // identical representations (size and alignment).
+      core::slice::from_raw_parts_mut(s.as_mut_ptr() as *mut Self, s.len())
+    }
+  }
+
+  /// Convert the wrapper type into the inner type.
+  #[inline]
+  fn unwrap(s: Self) -> Inner
+  where
+    Self: Sized,
+    Inner: Sized,
+  {
+    unsafe { transmute_copy(&s) }
+  }
+
+  /// Convert a reference to the wrapper type into a reference to the inner
+  /// type.
+  #[inline]
+  fn unwrap_ref(s: &Self) -> &Inner {
+    unsafe {
+      assert!(size_of::<*const Inner>() == size_of::<*const Self>());
+      // A pointer cast does't work here because rustc can't tell that
+      // the vtables match (because of the `?Sized` restriction relaxation).
+      // A `transmute` doesn't work because the sizes are unspecified.
+      //
+      // SAFETY: The unsafe contract requires that these two have
+      // identical representations.
+      let wrapper_ptr = s as *const Self;
+      let inner_ptr: *const Inner = transmute_copy(&wrapper_ptr);
+      &*inner_ptr
+    }
+  }
+
+  /// Convert a mutable reference to the wrapper type into a mutable reference
+  /// to the inner type.
+  #[inline]
+  fn unwrap_mut(s: &mut Self) -> &mut Inner {
+    unsafe {
+      assert!(size_of::<*mut Inner>() == size_of::<*mut Self>());
+      // A pointer cast does't work here because rustc can't tell that
+      // the vtables match (because of the `?Sized` restriction relaxation).
+      // A `transmute` doesn't work because the sizes are unspecified.
+      //
+      // SAFETY: The unsafe contract requires that these two have
+      // identical representations.
+      let wrapper_ptr = s as *mut Self;
+      let inner_ptr: *mut Inner = transmute_copy(&wrapper_ptr);
+      &mut *inner_ptr
+    }
+  }
+
+  /// Convert a slice to the wrapped type into a slice to the inner type.
+  #[inline]
+  fn unwrap_slice(s: &[Self]) -> &[Inner]
+  where
+    Self: Sized,
+    Inner: Sized,
+  {
+    unsafe {
+      assert!(size_of::<*const Inner>() == size_of::<*const Self>());
+      assert!(align_of::<*const Inner>() == align_of::<*const Self>());
+      // SAFETY: The unsafe contract requires that these two have
+      // identical representations (size and alignment).
+      core::slice::from_raw_parts(s.as_ptr() as *const Inner, s.len())
+    }
+  }
+
+  /// Convert a mutable slice to the wrapped type into a mutable slice to the
+  /// inner type.
+  #[inline]
+  fn unwrap_slice_mut(s: &mut [Self]) -> &mut [Inner]
+  where
+    Self: Sized,
+    Inner: Sized,
+  {
+    unsafe {
+      assert!(size_of::<*mut Inner>() == size_of::<*mut Self>());
+      assert!(align_of::<*mut Inner>() == align_of::<*mut Self>());
+      // SAFETY: The unsafe contract requires that these two have
+      // identical representations (size and alignment).
+      core::slice::from_raw_parts_mut(s.as_mut_ptr() as *mut Inner, s.len())
+    }
+  }
 }
 
 unsafe impl<T> TransparentWrapper<T> for core::num::Wrapping<T> {}


Test patch:
diff --git a/tests/cast_slice_tests.rs b/tests/cast_slice_tests.rs
index 9eb08c5..515a628 100644
--- a/tests/cast_slice_tests.rs
+++ b/tests/cast_slice_tests.rs
@@ -25,7 +25,8 @@ fn test_try_cast_slice() {
     Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned)
   );
 
-  // by taking one byte off the end, we're aligned but would have slop bytes for u32
+  // by taking one byte off the end, we're aligned but would have slop bytes for
+  // u32
   let the_bytes_len_minus1 = the_bytes.len() - 1;
   let slop_bytes = &the_bytes[..the_bytes_len_minus1];
   assert_eq!(
@@ -62,7 +63,8 @@ fn test_try_cast_slice_mut() {
     Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned)
   );
 
-  // by taking one byte off the end, we're aligned but would have slop bytes for u32
+  // by taking one byte off the end, we're aligned but would have slop bytes for
+  // u32
   let the_bytes_len_minus1 = the_bytes.len() - 1;
   let slop_bytes = &mut the_bytes[..the_bytes_len_minus1];
   assert_eq!(
@@ -92,7 +94,10 @@ fn test_types() {
 #[test]
 fn test_bytes_of() {
   assert_eq!(bytes_of(&0xaabbccdd_u32), &0xaabbccdd_u32.to_ne_bytes());
-  assert_eq!(bytes_of_mut(&mut 0xaabbccdd_u32), &mut 0xaabbccdd_u32.to_ne_bytes());
+  assert_eq!(
+    bytes_of_mut(&mut 0xaabbccdd_u32),
+    &mut 0xaabbccdd_u32.to_ne_bytes()
+  );
   let mut a = 0xaabbccdd_u32;
   let a_addr = &a as *const _ as usize;
   // ensure addresses match.
@@ -105,9 +110,18 @@ fn test_try_from_bytes() {
   let u32s = [0xaabbccdd, 0x11223344_u32];
   let bytes = bytemuck::cast_slice::<u32, u8>(&u32s);
   assert_eq!(try_from_bytes::<u32>(&bytes[..4]), Ok(&u32s[0]));
-  assert_eq!(try_from_bytes::<u32>(&bytes[..5]), Err(PodCastError::SizeMismatch));
-  assert_eq!(try_from_bytes::<u32>(&bytes[..3]), Err(PodCastError::SizeMismatch));
-  assert_eq!(try_from_bytes::<u32>(&bytes[1..5]), Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned));
+  assert_eq!(
+    try_from_bytes::<u32>(&bytes[..5]),
+    Err(PodCastError::SizeMismatch)
+  );
+  assert_eq!(
+    try_from_bytes::<u32>(&bytes[..3]),
+    Err(PodCastError::SizeMismatch)
+  );
+  assert_eq!(
+    try_from_bytes::<u32>(&bytes[1..5]),
+    Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned)
+  );
 }
 
 #[test]
@@ -117,9 +131,18 @@ fn test_try_from_bytes_mut() {
   let bytes = bytemuck::cast_slice_mut::<u32, u8>(&mut u32s);
   assert_eq!(try_from_bytes_mut::<u32>(&mut bytes[..4]), Ok(&mut abcd));
   assert_eq!(try_from_bytes_mut::<u32>(&mut bytes[..4]), Ok(&mut abcd));
-  assert_eq!(try_from_bytes_mut::<u32>(&mut bytes[..5]), Err(PodCastError::SizeMismatch));
-  assert_eq!(try_from_bytes_mut::<u32>(&mut bytes[..3]), Err(PodCastError::SizeMismatch));
-  assert_eq!(try_from_bytes::<u32>(&mut bytes[1..5]), Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned));
+  assert_eq!(
+    try_from_bytes_mut::<u32>(&mut bytes[..5]),
+    Err(PodCastError::SizeMismatch)
+  );
+  assert_eq!(
+    try_from_bytes_mut::<u32>(&mut bytes[..3]),
+    Err(PodCastError::SizeMismatch)
+  );
+  assert_eq!(
+    try_from_bytes::<u32>(&mut bytes[1..5]),
+    Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned)
+  );
 }
 
 #[test]
@@ -136,7 +159,10 @@ fn test_from_bytes_mut() {
   let a_addr = &a as *const _ as usize;
   let aligned_bytes = bytemuck::bytes_of_mut(&mut a);
   assert_eq!(*from_bytes_mut::<u32>(aligned_bytes), 0xaabbccdd_u32);
-  assert_eq!(from_bytes_mut::<u32>(aligned_bytes) as *const u32 as usize, a_addr);
+  assert_eq!(
+    from_bytes_mut::<u32>(aligned_bytes) as *const u32 as usize,
+    a_addr
+  );
 }
 
 // like #[should_panic], but can be a part of another test, instead of requiring
@@ -144,7 +170,10 @@ fn test_from_bytes_mut() {
 macro_rules! should_panic {
   ($ex:expr) => {
     assert!(
-      std::panic::catch_unwind(|| { let _ = $ex; }).is_err(),
+      std::panic::catch_unwind(|| {
+        let _ = $ex;
+      })
+      .is_err(),
       concat!("should have panicked: `", stringify!($ex), "`")
     );
   };
diff --git a/tests/derive.rs b/tests/derive.rs
index 1e1deeb..2524e6d 100644
--- a/tests/derive.rs
+++ b/tests/derive.rs
@@ -1,7 +1,7 @@
 #![cfg(feature = "derive")]
 #![allow(dead_code)]
 
-use bytemuck::{Zeroable, Pod, TransparentWrapper};
+use bytemuck::{Pod, TransparentWrapper, Zeroable};
 
 #[derive(Copy, Clone, Pod, Zeroable)]
 #[repr(C)]
@@ -21,5 +21,5 @@ struct TransparentSingle {
 #[transparent(u16)]
 struct TransparentWithZeroSized {
   a: u16,
-  b: ()
-}
\ No newline at end of file
+  b: (),
+}
diff --git a/tests/transparent.rs b/tests/transparent.rs
new file mode 100644
index 0000000..f0f9da8
--- /dev/null
+++ b/tests/transparent.rs
@@ -0,0 +1,64 @@
+// Currently this test doesn't actually check the output of the functions.
+// It's only here for miri to check for any potential undefined behaviour.
+// TODO: check function results
+
+#[test]
+fn test_transparent_wrapper() {
+  // An external type defined in a different crate.
+  #[derive(Copy, Clone, Default)]
+  struct Foreign(u8);
+
+  use bytemuck::TransparentWrapper;
+
+  #[derive(Copy, Clone)]
+  #[repr(transparent)]
+  struct Wrapper(Foreign);
+
+  unsafe impl TransparentWrapper<Foreign> for Wrapper {}
+
+  // Traits can be implemented on crate-local wrapper.
+  unsafe impl bytemuck::Zeroable for Wrapper {}
+  unsafe impl bytemuck::Pod for Wrapper {}
+
+  let _: u8 = bytemuck::cast(Wrapper::wrap(Foreign::default()));
+  let _: Foreign = Wrapper::unwrap(bytemuck::cast(u8::default()));
+
+  let _: &u8 = bytemuck::cast_ref(Wrapper::wrap_ref(&Foreign::default()));
+  let _: &Foreign = Wrapper::unwrap_ref(bytemuck::cast_ref(&u8::default()));
+
+  let _: &mut u8 =
+    bytemuck::cast_mut(Wrapper::wrap_mut(&mut Foreign::default()));
+  let _: &mut Foreign =
+    Wrapper::unwrap_mut(bytemuck::cast_mut(&mut u8::default()));
+
+  let _: &[u8] =
+    bytemuck::cast_slice(Wrapper::wrap_slice(&[Foreign::default()]));
+  let _: &[Foreign] =
+    Wrapper::unwrap_slice(bytemuck::cast_slice(&[u8::default()]));
+
+  let _: &mut [u8] =
+    bytemuck::cast_slice_mut(Wrapper::wrap_slice_mut(
+      &mut [Foreign::default()],
+    ));
+  let _: &mut [Foreign] =
+    Wrapper::unwrap_slice_mut(bytemuck::cast_slice_mut(&mut [u8::default()]));
+
+  let _: &[u8] = bytemuck::bytes_of(Wrapper::wrap_ref(&Foreign::default()));
+  let _: &Foreign = Wrapper::unwrap_ref(bytemuck::from_bytes(&[u8::default()]));
+
+  let _: &mut [u8] =
+    bytemuck::bytes_of_mut(Wrapper::wrap_mut(&mut Foreign::default()));
+  let _: &mut Foreign =
+    Wrapper::unwrap_mut(bytemuck::from_bytes_mut(&mut [u8::default()]));
+
+  // not sure if this is the right usage
+  let _ =
+    bytemuck::pod_align_to::<_, u8>(Wrapper::wrap_slice(&[Foreign::default()]));
+  // counterpart?
+
+  // not sure if this is the right usage
+  let _ = bytemuck::pod_align_to_mut::<_, u8>(Wrapper::wrap_slice_mut(&mut [
+    Foreign::default(),
+  ]));
+  // counterpart?
+}