diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index ef7b3b1d1470a..9f8fbaa95ae6b 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -43,8 +43,9 @@ impl<T: ?Sized> *const T {
     #[stable(feature = "ptr_cast", since = "1.38.0")]
     #[rustc_const_stable(feature = "const_ptr_cast", since = "1.38.0")]
     #[inline]
-    pub const fn cast<U>(self) -> *const U {
-        self as _
+    pub const fn cast<U: ?Sized + Thin>(self) -> *const U {
+        // TODO: Just use `self as _` when the compiler understands `Thin`
+        from_raw_parts(self as *const (), ())
     }
 
     /// Use the pointer value in a new pointer of another type.
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index 6a3b9ee9a7daa..fd0ebbff48a67 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -41,9 +41,10 @@ impl<T: ?Sized> *mut T {
     /// Casts to a pointer of another type.
     #[stable(feature = "ptr_cast", since = "1.38.0")]
     #[rustc_const_stable(feature = "const_ptr_cast", since = "1.38.0")]
-    #[inline(always)]
-    pub const fn cast<U>(self) -> *mut U {
-        self as _
+    #[inline]
+    pub const fn cast<U: ?Sized + Thin>(self) -> *mut U {
+        // TODO: Just use `self as _` when the compiler understands `Thin`
+        from_raw_parts_mut(self as *mut (), ())
     }
 
     /// Use the pointer value in a new pointer of another type.
diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs
index f3ef094cbccc5..872c4b2530795 100644
--- a/library/core/src/ptr/non_null.rs
+++ b/library/core/src/ptr/non_null.rs
@@ -6,7 +6,7 @@ use crate::marker::Unsize;
 use crate::mem::{self, MaybeUninit};
 use crate::num::NonZeroUsize;
 use crate::ops::{CoerceUnsized, DispatchFromDyn};
-use crate::ptr::Unique;
+use crate::ptr::{Thin, Unique};
 use crate::slice::{self, SliceIndex};
 
 /// `*mut T` but non-zero and [covariant].
@@ -450,9 +450,9 @@ impl<T: ?Sized> NonNull<T> {
     #[must_use = "this returns the result of the operation, \
                   without modifying the original"]
     #[inline]
-    pub const fn cast<U>(self) -> NonNull<U> {
+    pub const fn cast<U: ?Sized + Thin>(self) -> NonNull<U> {
         // SAFETY: `self` is a `NonNull` pointer which is necessarily non-null
-        unsafe { NonNull::new_unchecked(self.as_ptr() as *mut U) }
+        unsafe { NonNull::new_unchecked(self.as_ptr().cast()) }
     }
 }
 
diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs
index 97a369810056d..9fc7c4448fb2f 100644
--- a/library/core/tests/ptr.rs
+++ b/library/core/tests/ptr.rs
@@ -106,6 +106,40 @@ fn test_is_null() {
     assert!(em.is_null());
 }
 
+#[test]
+fn test_cast() {
+    // `cast` works like `as` for unsized types
+    let cs: *const [u8] = &[1, 2, 3];
+    assert_eq!(cs.cast::<u8>(), cs as *const u8);
+
+    let ms: *mut [u8] = &mut [1, 2, 3];
+    assert_eq!(ms.cast::<u8>(), ms as *mut u8);
+
+    let ci: *const dyn ToString = &3;
+    assert_eq!(ci.cast::<u8>(), ci as *const u8);
+
+    let mi: *mut dyn ToString = &mut 3;
+    assert_eq!(mi.cast::<u8>(), mi as *mut u8);
+
+    // `cast` can be used to cast between extern types
+    extern "C" {
+        type Extern1;
+        type Extern2;
+    }
+
+    let p: *const u8 = &42;
+    let e: *const Extern1 = p.cast();
+    assert_eq!(e.cast::<Extern2>(), e as *const Extern2);
+
+    let p: *mut u8 = &mut 42;
+    let e: *mut Extern1 = p.cast();
+    assert_eq!(e.cast::<Extern2>(), e as *mut Extern2);
+
+    let p: NonNull<u8> = NonNull::from(&42);
+    let e: NonNull<Extern1> = p.cast();
+    assert_eq!(Some(e.cast::<Extern2>()), NonNull::new(e.as_ptr() as *mut Extern2));
+}
+
 #[test]
 fn test_as_ref() {
     unsafe {
@@ -485,8 +519,8 @@ fn ptr_metadata() {
     let () = metadata(&[4, 7]);
     let () = metadata(&(4, String::new()));
     let () = metadata(&Pair(4, String::new()));
-    let () = metadata(ptr::null::<()>() as *const Extern);
-    let () = metadata(ptr::null::<()>() as *const <&u32 as std::ops::Deref>::Target);
+    let () = metadata(ptr::null::<()>().cast::<Extern>());
+    let () = metadata(ptr::null::<()>().cast::<<&u32 as std::ops::Deref>::Target>());
 
     assert_eq!(metadata("foo"), 3_usize);
     assert_eq!(metadata(&[4, 7][..]), 2_usize);