Skip to content

Commit 3bd9327

Browse files
authored
Merge pull request #14 from cuviper/primitive-bytes
Add trait `PrimitiveBytes` for `PrimitiveNumber::Bytes`
2 parents 238ec47 + d39d087 commit 3bd9327

File tree

5 files changed

+144
-23
lines changed

5 files changed

+144
-23
lines changed

Cargo.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "num-primitive"
3-
version = "0.3.2"
3+
version = "0.3.3"
44
description = "Traits for primitive numeric types"
55
repository = "https://github.com/rust-num/num-primitive"
66
license = "MIT OR Apache-2.0"
@@ -9,6 +9,10 @@ categories = ["algorithms", "science", "no-std"]
99
edition = "2024"
1010
rust-version = "1.91"
1111

12+
[package.metadata.release]
13+
allow-branch = ["main"]
14+
sign-tag = true
15+
1216
[features]
1317
default = ["std"]
1418
std = []
@@ -24,7 +28,3 @@ unreachable-pub = "deny"
2428
[lints.rustdoc]
2529
broken-intra-doc-links = "deny"
2630
private-intra-doc-links = "deny"
27-
28-
[package.metadata.release]
29-
allow-branch = ["main"]
30-
sign-tag = true

RELEASES.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# Release 0.3.3 (2025-12-17)
2+
3+
- Added `PrimitiveBytes` to consolidate the constraints on `PrimitiveNumber::Bytes`
4+
- Extended with `IndexMut<usize>`, and `PartialEq` and `TryFrom` with slice refs.
5+
- Added array construction methods `from_fn` and `repeat`.
6+
17
# Release 0.3.2 (2025-12-16)
28

39
- Updated to MSRV 1.91.

src/bytes.rs

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
use core::array::TryFromSliceError;
2+
3+
trait Sealed {}
4+
5+
/// Trait for arrays of bytes that may be used in numeric conversions.
6+
///
7+
/// In particular, this is used as a bound for the associated type
8+
/// [`PrimitiveNumber::Bytes`][crate::PrimitiveNumber::Bytes] for converting numbers to and from an
9+
/// array of bytes in various endian orders. It is simply `[u8; size_of::<Self>()]` for every
10+
/// primitive number type, but there's no way yet to write that directly in the trait.
11+
///
12+
/// This trait is not exhaustive of everything byte arrays can do, but it's enough to be useful for
13+
/// generically constructing bytes and dealing with them as slices.
14+
///
15+
/// This trait is sealed with a private trait to prevent downstream implementations, so we may
16+
/// continue to expand along with the standard library without worrying about breaking changes for
17+
/// implementors.
18+
///
19+
/// # Examples
20+
///
21+
/// The supertraits of `PrimitiveBytes` can be used without importing this trait directly:
22+
///
23+
/// ```
24+
/// use num_primitive::PrimitiveNumber;
25+
///
26+
/// // Return a value with the most significant bit set
27+
/// fn msb<T: PrimitiveNumber>() -> T {
28+
/// let mut bytes = T::Bytes::default(); // prelude `Default`
29+
/// bytes[0] = 0x80; // operator `IndexMut`
30+
/// T::from_be_bytes(bytes)
31+
/// }
32+
///
33+
/// assert_eq!(msb::<i64>(), i64::MIN);
34+
/// assert_eq!(msb::<u16>(), 1u16 << 15);
35+
/// assert!(msb::<f64>().total_cmp(&-0.0).is_eq());
36+
/// ```
37+
///
38+
/// However, this trait must be imported to use its own methods like [`repeat`][Self::repeat]:
39+
///
40+
/// ```
41+
/// use num_primitive::{PrimitiveBytes, PrimitiveNumber};
42+
///
43+
/// // Return a value with all bits set
44+
/// fn all_ones<T: PrimitiveNumber>() -> T {
45+
/// T::from_ne_bytes(T::Bytes::repeat(0xff))
46+
/// }
47+
///
48+
/// assert_eq!(all_ones::<i32>(), -1);
49+
/// assert_eq!(all_ones::<usize>(), usize::MAX);
50+
/// assert!(all_ones::<f64>().is_nan());
51+
/// ```
52+
///
53+
/// In cases where the size is known, you can use that as a constraint and then work with byte
54+
/// arrays directly, regardless of this trait.
55+
///
56+
/// ```
57+
/// use num_primitive::PrimitiveNumber;
58+
///
59+
/// fn rust<T: PrimitiveNumber<Bytes = [u8; 4]>>() -> T {
60+
/// T::from_be_bytes(*b"Rust")
61+
/// }
62+
///
63+
/// assert_eq!(rust::<i32>(), 0x52_75_73_74_i32);
64+
/// assert_eq!(rust::<u32>(), 0x52_75_73_74_u32);
65+
/// assert_eq!(rust::<f32>(), 2.63551e11);
66+
/// ```
67+
#[expect(private_bounds)]
68+
pub trait PrimitiveBytes:
69+
'static
70+
+ Sealed
71+
+ core::borrow::Borrow<[u8]>
72+
+ core::borrow::BorrowMut<[u8]>
73+
+ core::cmp::Eq
74+
+ core::cmp::Ord
75+
+ core::cmp::PartialEq<[u8]>
76+
+ core::convert::AsRef<[u8]>
77+
+ core::convert::AsMut<[u8]>
78+
+ core::default::Default
79+
+ core::fmt::Debug
80+
+ core::hash::Hash
81+
+ core::marker::Copy
82+
+ core::marker::Send
83+
+ core::marker::Sync
84+
+ core::marker::Unpin
85+
+ core::ops::Index<usize, Output = u8>
86+
+ core::ops::IndexMut<usize>
87+
+ core::panic::RefUnwindSafe
88+
+ core::panic::UnwindSafe
89+
+ for<'a> core::cmp::PartialEq<&'a [u8]>
90+
+ for<'a> core::cmp::PartialEq<&'a mut [u8]>
91+
+ for<'a> core::convert::TryFrom<&'a [u8], Error = TryFromSliceError>
92+
+ for<'a> core::convert::TryFrom<&'a mut [u8], Error = TryFromSliceError>
93+
{
94+
/// Creates an array of bytes where each byte is produced by calling `f`
95+
/// with that element's index while walking forward through the array.
96+
///
97+
/// See the [`core::array::from_fn`] function.
98+
fn from_fn<F>(f: F) -> Self
99+
where
100+
F: FnMut(usize) -> u8;
101+
102+
/// Creates an array of bytes with a repeat expression, `[value; N]`.
103+
fn repeat(value: u8) -> Self;
104+
}
105+
106+
macro_rules! impl_bytes {
107+
($($N:literal),+) => {$(
108+
impl Sealed for [u8; $N] {}
109+
110+
impl PrimitiveBytes for [u8; $N] {
111+
#[inline]
112+
fn from_fn<F>(f: F) -> Self
113+
where
114+
F: FnMut(usize) -> u8
115+
{
116+
core::array::from_fn(f)
117+
}
118+
119+
#[inline]
120+
fn repeat(value: u8) -> Self {
121+
// We don't need to forward to `array::repeat` for cloning,
122+
// since we can construct it directly with `u8` copies.
123+
[value; $N]
124+
}
125+
}
126+
)+}
127+
}
128+
129+
impl_bytes!(1, 2, 4, 8, 16);

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ extern crate std;
5959
#[macro_use]
6060
mod macros;
6161

62+
mod bytes;
6263
mod error;
6364
mod float;
6465
mod integer;
@@ -69,6 +70,7 @@ mod unsigned;
6970
#[cfg(test)]
7071
mod tests;
7172

73+
pub use self::bytes::PrimitiveBytes;
7274
pub use self::error::PrimitiveError;
7375
pub use self::float::{PrimitiveFloat, PrimitiveFloatRef, PrimitiveFloatToInt};
7476
pub use self::integer::{PrimitiveInteger, PrimitiveIntegerRef};

src/number.rs

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::PrimitiveError;
1+
use crate::{PrimitiveBytes, PrimitiveError};
22

33
trait Sealed {}
44
struct SealedToken;
@@ -102,23 +102,7 @@ pub trait PrimitiveNumber:
102102
{
103103
/// An array of bytes used by methods like [`from_be_bytes`][Self::from_be_bytes] and
104104
/// [`to_be_bytes`][Self::to_be_bytes]. It is effectively `[u8; size_of::<Self>()]`.
105-
type Bytes: 'static
106-
+ core::borrow::Borrow<[u8]>
107-
+ core::borrow::BorrowMut<[u8]>
108-
+ core::cmp::Eq
109-
+ core::cmp::Ord
110-
+ core::cmp::PartialEq<[u8]>
111-
+ core::convert::AsRef<[u8]>
112-
+ core::convert::AsMut<[u8]>
113-
+ core::default::Default
114-
+ core::fmt::Debug
115-
+ core::hash::Hash
116-
+ core::marker::Copy
117-
+ core::marker::Send
118-
+ core::marker::Sync
119-
+ core::marker::Unpin
120-
+ core::panic::RefUnwindSafe
121-
+ core::panic::UnwindSafe;
105+
type Bytes: PrimitiveBytes;
122106

123107
/// Creates a number from its representation as a byte array in big endian.
124108
fn from_be_bytes(bytes: Self::Bytes) -> Self;

0 commit comments

Comments
 (0)