11---
22title : ' Niches for integer types in Rust'
3- publishDate : ' 2026-05-15'
4- updatedAt : ' 2026-05-15'
5- draft : true
3+ publishDate : ' 2026-05-04'
4+ updatedAt : ' 2026-05-04'
65categories :
76- rust
87- type-system
@@ -140,7 +139,7 @@ impl Pos<Zero> {
140139This works, and ` Option<Pos<Zero>> ` is now 4 bytes.
141140But every ` new ` adds 1 and every ` get ` subtracts 1.
142141It's a single ALU instruction each time,
143- so the cost is negligible in practice .
142+ so the cost is probably negligible in most code paths .
144143Still, it's conceptually unsatisfying:
145144we're contorting the representation
146145to fit a niche that doesn't match our actual invariant.
@@ -153,92 +152,40 @@ to fit a niche that doesn't match our actual invariant.
153152As of Rust 1.95 (May 2026),
154153there is no stable way to tell the compiler
155154"this ` u32 ` only holds values ` 0..=0x7FFF_FFFF ` ."
156- But internally, the standard library does exactly that for its own types
157- using the attributes
158- ` rustc_layout_scalar_valid_range_start ` and ` rustc_layout_scalar_valid_range_end ` .
159- On nightly, we can use them too:
160-
161- ``` rust {.wide}
162- #![feature(rustc_attrs)]
163-
164- #[rustc_layout_scalar_valid_range_start(0)]
165- #[rustc_layout_scalar_valid_range_end(0x7FFF_FFFF)] // i32::MAX
166- #[repr(transparent)]
167- #[derive(Clone , Copy , PartialEq , Eq , PartialOrd , Ord , Hash )]
168- pub struct Pos (u32 );
169- ```
170-
171- This tells the compiler the full valid range,
172- and every bit pattern outside it becomes a niche.
173- No bias, no XOR, no runtime cost at all.
174- The ` get ` accessor is a plain field read:
175-
176- ``` rust {.wide}
177- impl TryFrom <i32 > for Pos {
178- type Error = ();
179-
180- fn try_from (value : i32 ) -> Result <Self , Self :: Error > {
181- if value < 0 { return Err (()); }
182- // SAFETY: value is in 0..=i32::MAX
183- Ok (unsafe { Self (value as u32 ) })
184- }
185- }
186155
187- impl Pos {
188- pub const fn get (self ) -> i32 {
189- // zero-cost: the u32 is already in range
190- self . 0 as i32
191- }
192- }
193- ```
194-
195- And we get the niche for free:
196-
197- ``` rust {.wide}
198- assert_eq! (size_of :: <Pos >(), 4 );
199- assert_eq! (size_of :: <Option <Pos >>(), 4 );
200- ```
201-
202- You can see a [ little test script] [ play1 ] here.
203-
204- [ play1 ] : https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=f5b9731872fc5762c2c3fba0c1af6007 " Rust playground "
205-
206- There's a catch, though:
207- these attributes make every construction of the type ` unsafe ` .
208- Any code that writes to the inner ` u32 `
209- must uphold the range invariant,
210- and the compiler won't check it for you.
211- That's fine when it's behind a ` new ` constructor,
212- but it does mean we need to restrict how this can be used.
156+ ~~ But internally, the standard library does exactly that for its own types
157+ using the attributes
158+ ` rustc_layout_scalar_valid_range_start ` and ` rustc_layout_scalar_valid_range_end ` .~~
213159
214- These attributes are not a public API.
215- They are clearly marked as ` rustc_attrs `
216- which sounds * very* internal.
160+ ** Oh wait -- while I was writing this post,
161+ this exact feature got replaced!**
217162
218163## On nightly: Pattern types
219164
220165While researching this,
221166I came across [ this issue] [ rust-135996 ]
222- where Oli proposes using a "pattern types" feature.
223- So it seems there is * another * way of doing this!
224- While reading through the [ tracking issue] [ rust-123646 ] and Zulip thread ,
167+ where [ Oli] proposes using a "pattern types" feature.
168+ So it seems there is new way of doing this!
169+ While reading through the [ tracking issue] [ rust-123646 ] and [ Zulip channel ] ,
225170I found this [ pre-RFC document] [ pre-rfc ]
226171(last updated in 2024 but discussed further in 2025).
227172What is in the standard library right now
228173on nightly is a [ ` pattern_type! ` ] macro.
229174[ Rust PR 136006] has some usage of this so I could put this together:
230175
176+ [ Oli ] : https://github.com/oli-obk
231177[ rust-135996 ] : https://github.com/rust-lang/rust/issues/135996 " Replace rustc_layout_scalar_valid_range_start attribute with pattern types "
232- [ rust-123646 ] : https://github.com/rust-lang/rust/issues/123646 " Tracking Issue for pattern types "
178+ [ rust-123646 ] : https://github.com/rust-lang/rust/issues/123646
233179[ pre-rfc ] : https://gist.github.com/joboet/0cecbce925ee2ad1ee3e5520cec81e30
234180[ `pattern_type!` ] : https://doc.rust-lang.org/1.95.0/core/macro.pattern_type.html " core::pattern_type! "
235181[ Rust PR 136006 ] : https://github.com/rust-lang/rust/pull/136006
182+ [ Zulip channel ] : https://rust-lang.zulipchat.com/#narrow/channel/481660-t-lang.2Fpattern-types
236183
237184``` rust {.wide}
238185#![feature(pattern_types)]
239186#![feature(pattern_type_macro)]
240187
241- pub struct Pos (pattern_type! (i32 is 0 .. ));
188+ pub struct Pos (pattern_type! (i32 is 0 ..= i32 :: MAX ));
242189
243190impl Pos {
244191 pub const fn new (value : i32 ) -> Option <Self > {
@@ -261,22 +208,18 @@ impl Pos {
261208
262209[ play2 ] : https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=d4c7bf6936d2bcef89455e7e271fba76
263210
264- I'm not sure about the ` transmute ` being the * best* way to do this,
265- but ` pattern_type! ` produces a new type
266- and I couldn't figure out how to construct it otherwise.
267-
268- Given the lack of actual documentation and RFC[ ^ missed ] ,
269- I think it's fair to classify this feature
270- as even more nightly than the other one.
271-
272- [ ^ missed ] : Or did I miss it?
211+ For now, ` transmute ` from the underlying type is the only way
212+ to construct the pattern type.
213+ On Zulip, Oli also recommended using inclusive ranges.
273214
274215## Conclusion
275216
276217I'm not using any of these nightly features in the real code yet,
277218but I'm glad to see momentum in this space.
278219It's a feature I've wanted in a few places already.
279- Another use case is a proper type for an "inline length" type,
280- removing a workaround like the one ` SmolStr ` uses [ here] [ smol_str len ] .
220+ Other use cases for patterns type are an "inline length" type,
221+ removing a workaround like the one ` SmolStr ` uses [ here] [ smol_str len ] ,
222+ or types that have sentinels by specification,
223+ like ` INT8 ` in BAM files which actuallys is ` -120..=127 ` .
281224
282225[ smol_str len ] : https://github.com/rust-lang/rust-analyzer/blob/4a244d4c6bf18bae57626dcaf81bf6442ad59380/lib/smol_str/src/lib.rs#L541-L569
0 commit comments