From 4b07101023f65a6a72440b31932fe0b7778fda97 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Tue, 15 Oct 2024 10:53:55 -0700 Subject: [PATCH 1/2] Document that `NotNan` is `repr(transparent)`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, rustdoc only displays `repr(transparent)` if the type’s fields are public, so there is no indication in the documentation that `NotNan` has `repr(transparent)`. Even if it was visible, there is not currently a strong consensus that such a repr may be assumed to be a stable part of the public API. Therefore, add paragraphs to `NotNan` and `OrderedFloat`’s documentation guaranteeing the repr. I also mentioned `bytemuck` so as to not be encouraging unsafe code, though the phrasing isn’t great. --- src/lib.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 930daef..3d27509 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,6 +75,18 @@ fn canonicalize_signed_zero(x: T) -> T { /// s.insert(OrderedFloat(NAN)); /// assert!(s.contains(&OrderedFloat(NAN))); /// ``` +/// +/// # Representation +/// +/// `OrderedFloat` has `#[repr(transparent)]` and permits any value, so it is sound to use +/// [transmute](core::mem::transmute) or pointer casts to convert between any type `T` and +/// `OrderedFloat`. +/// However, consider using [`bytemuck`] as a safe alternative if possible. +/// +#[cfg_attr( + not(feature = "bytemuck"), + doc = "[`bytemuck`]: https://docs.rs/bytemuck/1/" +)] #[derive(Default, Clone, Copy)] #[repr(transparent)] pub struct OrderedFloat(pub T); @@ -1157,6 +1169,18 @@ impl Num for OrderedFloat { /// // This will panic: /// let c = a + b; /// ``` +/// +/// # Representation +/// +/// `NotNan` has `#[repr(transparent)]`, so it is sound to use +/// [transmute](core::mem::transmute) or pointer casts to convert between any type `T` and +/// `NotNan`, as long as this does not create a NaN value. +/// However, consider using [`bytemuck`] as a safe alternative if possible. +/// +#[cfg_attr( + not(feature = "bytemuck"), + doc = "[`bytemuck`]: https://docs.rs/bytemuck/1/" +)] #[derive(PartialOrd, PartialEq, Default, Clone, Copy)] #[repr(transparent)] pub struct NotNan(T); From 7abdbc96a734ce19a8564d3487f6d4b0e176e3e7 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Tue, 15 Oct 2024 11:00:00 -0700 Subject: [PATCH 2/2] Add `impl bytemuck::TransparentWrapper for OrderedFloat`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This gives a more strongly-typed way for users to take advantage of `OrderedFloat`’s representation. --- src/lib.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3d27509..38790b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -78,7 +78,7 @@ fn canonicalize_signed_zero(x: T) -> T { /// /// # Representation /// -/// `OrderedFloat` has `#[repr(transparent)]` and permits any value, so it is sound to use +/// `OrderedFloat` has `#[repr(transparent)]` and permits any value, so it is sound to use /// [transmute](core::mem::transmute) or pointer casts to convert between any type `T` and /// `OrderedFloat`. /// However, consider using [`bytemuck`] as a safe alternative if possible. @@ -2754,7 +2754,7 @@ mod impl_arbitrary { #[cfg(feature = "bytemuck")] mod impl_bytemuck { use super::{FloatCore, NotNan, OrderedFloat}; - use bytemuck::{AnyBitPattern, CheckedBitPattern, NoUninit, Pod, Zeroable}; + use bytemuck::{AnyBitPattern, CheckedBitPattern, NoUninit, Pod, TransparentWrapper, Zeroable}; unsafe impl Zeroable for OrderedFloat {} @@ -2776,6 +2776,10 @@ mod impl_bytemuck { } } + // OrderedFloat allows any value of the contained type, so it is a TransparentWrapper. + // NotNan does not, so it is not. + unsafe impl TransparentWrapper for OrderedFloat {} + #[test] fn test_not_nan_bit_pattern() { use bytemuck::checked::{try_cast, CheckedCastError};