5 ms·
I wonder, why is it NonZeroU8, NonZeroU16, etc instead of something like NonZero<u8>, NonZero<u16>, etc? In theory, in the future, when Rust supports constants
by ainar-g 8y ago
I wonder, why is it NonZeroU8, NonZeroU16, etc instead of something like NonZero<u8>, NonZero<u16>, etc?
In theory, in the future, when Rust supports constants as template arguments, one might even add a const argument, the value that should be used as "null". E.g. if my u8 never reaches 255, I could use something like Never<u8, 255>.
- Arnavion 8y ago>I wonder, why is it NonZeroU8, NonZeroU16, etc instead of something like NonZero<u8>, NonZero<u16>, etc? The RFC covers this. https://github.com/rust-lang/rfcs/blob/master/text/2307-concrete-nonzero-types.md https://github.com/rust-lang/rfcs/blob/master/text/2307-conc... Also see the RFC discussion at https://github.com/rust-lang/rfcs/pull/2307 https://github.com/rust-lang/rfcs/pull/2307 Basically the generic NonZero already existed, but it's hard to express safely when used with arbitrary user types. >In theory, in the future, when Rust supports constants as template arguments, one might even add a const argument, the value that should be used as "null". E.g. if my u8 never reaches 255, I could use something like Never<u8, 255>. This requires const generics, so it could happen when they're implemented. (This is also mentioned in the RFC discussion.)
- lmm 8y agoDoes "const generics" essentially mean singleton types?
- steveklabnik 8y agoThere are so many "slightly different but mostly the same" things in this space. In Rust, this is const generics: https://github.com/rust-lang/rfcs/blob/master/text/2000-const-generics.md https://github.com/rust-lang/rfcs/blob/master/text/2000-cons... struct RectangularArray<T, const WIDTH: usize, const HEIGHT: usize> { array: [[T; WIDTH]; HEIGHT], } const X: usize = 7; fn main() { let x: RectangularArray<i32, 2, 4>; let y: RectangularArray<i32, X, {2 * 2}>; } I haven't read enough about singleton types to know the exact details about their similarities, so I'm not sure.
- lordnaikon 8y agoIs this what is known as pi or dependent types?
- steveklabnik 8y agoI believe that this initial version is not quite fully pi types; we did have an RFC for those but it was deemed too large to start off with.
- eddyb 8y agoYes, but limited to being applied at compile-time, with constant arguments. Fully general dependent type would allow passing in runtime values, which is significantly harder to type-check and execute
- steveklabnik 8y agoWe used to have NonZero<T>, but changed it https://github.com/rust-lang/rfcs/blob/b728bf68703f681ef0e42216cf31c224d4868caa/text/2307-concrete-nonzero-types.md https://github.com/rust-lang/rfcs/blob/b728bf68703f681ef0e42... The real motivation is > With NonNull covering pointers, the remaining use cases for NonZero are integers. It's unclear how extensible we truly want this to be, and so we decided to go with the concrete types. In the worst case, we'd eventually re-introduce some sort of NonZero<T>, and deprecate these types. This was deemed worth it to let people use this important feature on stable, rather than waiting and seeing if we ever decide to do otherwise.
- asdkhadsj 8y agoCan anyone brief what NonZero even is.. or rather, it's application? From the sounds of it, it's literally just a u8 (or w/e) that can't be zero.. but what use case does that have?
- steveklabnik 8y agoLet's take an enum, like Option: enum Option<T> { Some(T), None, } Under the hood, this is a "tagged union"; Rust has to have enough space for the T, but also for a tag, to say which variant is the current one. If T is a NonZero type, then we know that zero is never a valid value. That means that instead, Rust can use the zero value to mean the None case, and any other value to be the Some case, completely eliminating the extra tag, and reducing the size of the Option. This is a good example of a "zero-cost abstraction"; an Option in this case has absolutely no extra associated overhead at runtime. Note that these optimizations aren't specialized for Option; they apply to anything that has this kind of shape.
- asdkhadsj 8y agoThat's actually really cool! Thanks for the explanation
- steveklabnik 8y agoNo problem!
- monocasa 8y agoIs NonZero* special cased by the language? If for instance, I had a preexisting binary interface where 0 through (2^n - 2) is valid, but all bits set is the invalid case, can I explain that to the compiler as well?
- steveklabnik 8y agoYes, it is. The case you're talking about is pertinent to the above discussion on why it's NonZero* vs NonZero<T>; you cannot currently inform the compiler in your case.