4 ms·
Asserts are convenient, but every time I encounter them I ask "Can the compiler prove this can't happen?" In the example of "min(ages) > 0", making age a NonZe
by phamilton 2y ago
Asserts are convenient, but every time I encounter them I ask "Can the compiler prove this can't happen?"
In the example of "min(ages) > 0", making age a NonZero type renders the assert unnecessary. Rust even has some fancy perf optimizations it can do with that information. It's a win all around.
- winter_blue 2y agoIf you created a custom NonZero type, how would the Rust compiler figure out how to optimize that? How would one communicate properties/traits of a custom type (like a non-zero unit) that a compiler can leverage to optimize (in general, for any programming language)?
- dwattttt 2y agoI don't think the mechanics of being able to convey a niche are stable/developer-accessible, but NonZero is a type provided by the standard library: https://doc.rust-lang.org/std/num/struct.NonZero.html https://doc.rust-lang.org/std/num/struct.NonZero.html An example of the niche optimisations is in that link; if a 32bit number is NonZero, you can put that in an Option, and your Option<NonZero<u32>> will be the same size as a normal u32.
- phamilton 2y agoAn interesting project for niche types: https://github.com/rick-de-water/nonany https://github.com/rick-de-water/nonany It's not perfect but does allow some flexibility.
- IshKebab 2y agoIn general, you need a type system that supports sets of integer values, e.g. range(2, 7) or set(3, 5, 7). Rust doesn't support that unfortunately so it has a special annotation instead to make NonZero work.
- jiggawatts 2y agoIn terms of category theory, what we would need is subtraction and division types on top of product and sum types. So a 32-bit integer is the product of 32 two-state bit types. Something akin to NonZero could be defined as that type minus one state, such that there are now 4294967296 - 1 representable values. Similarly, pointer types on some machines always have some bits set to 0 due to hardware constraints. These can be represented in the type system as 2^64 / 2^n where 'n' is the number of bits that are not usable, resulting in something like 2^46 for typical CPUs. This would allow extra bits of state to be "packed in there". This concept is more generally useful, not just for bit-packing. For example, a database table row might be represented by a struct that contains a field for every column. But then, how to represent the result of a SELECT query!? Either you code-gen this into a new type, create that type manually, or use compiler-generated "unspeakable" type names. (Linq in C# does this.) Or... the language could natively support type division, so you could use one struct type for "all columns" and then use division on the type to delete columns you don't need for a particular query or REST response. There's a whole ecosystem of tools that work around the problem of not having this as a built-in feature in typical languages: AutoMapper, automap, Mapster, etc...
- justincormack 2y agoAda has constrained range types. Idris has lots more fun stuff with types. But not very mainstream yet.
- IshKebab 2y agoAs far as I understand it there's no need for that. You don't need to say [0, 256) except 0; you can just say [1, 256).
- Lvl999Noob 2y agoWith subtraction, you can say [0, 256) except [4, 9, 15, 26]. You can do that just by narrowing a single range. But then, its kinda hard to optimise such a split range in the general case. The narrowing option gives you 80% (maybe more) of the performance at 20% (probably even less) of the cost.
- Timshel 2y agoIn general such a thing is written with a wrapper type which disappear at compilation (a kind of stricter type alias). In rust might be a NewType: https://www.howtocodeit.com/articles/ultimate-guide-rust-newtypes https://www.howtocodeit.com/articles/ultimate-guide-rust-new...