[FEATURE] Turn as much as possible of `NumberFormat` into `const fn`
Evrey opened this issue · comments
Problem
This is how I currently define my number format at comptime, because const fn
is not supported:
const FORMI_LITERAL: NumberFormat =
NumberFormat::from_bits_truncate(0
| ((b'_' as u64) << 56) // digit_separator_to_flags
| 0x00000000_00000007 // REQUIRED_DIGITS
| 0x00000000_00000100 // NO_EXPONENT_WITHOUT_FRACTION
| 0x00000000_00000200 // NO_SPECIAL
| 0x00000111_00000000 // INTERNAL_DIGIT_SEPARATOR
);
I.e. a bunch of magic numbers that may break on any version jump.
Solution
Well, use const fn
. =) Or provide alternative functions using const fn
. Where checks are needed, a few different approaches are possible, each with ups and downs:
- Make the functions
unsafe fn with_×××_unchecked
andpanic!()
once panicing inconst fn
s is a thing. - Use the const assert trick to check invariants. I.e. index a const array with a bool. If it's true, you index out of bounds. Fetched value must be used, iirc.
- Use a crate for static assertions.
Prerequisites
- lexical version :
0.7.*
- lexical compilation features used:
correct
,radix
,format
Alternatives
Provide a single const fn
that returns true
if a format spec is valid, otherwise false
. Users of lexical-core
can then themselves do the static assertion. However, this still makes constructing a format at comptime »magic«, as opposed to using const fn
methods describing what it is one wants.
This is not impossible; however, it is mutually exclusive with the current implementation of dynamic, global-state, control character configuration.
I am not willing to commit at this time to making NumberFormat
ignore these configuration values; however, there's certainly a case for migrating as much of the API as possible to const fn
and having sibling non-const
runtime APIs that reflect this state. Additionally, #45 (also by you! excellent) would make it so that no such state occurs, and all behavior is parametric at call time.
I've pushed my current work to the feature/48
branch. I will likely not revisit this before August either, but it is on my list. Thank you again for the request and your patience
Sounds good to me. =)
@RazrFalcon This is currently done as of commit 022bfdb. Please ignore the broken test suite: I also worked on a few other issues (including #40, #45, and #58), which required refactoring a lot of internals (which completely broke the test suite due to new function signatures, none of the test suites will currently compile).
I'm working on fixing the tests now, but this took a lot longer to do satisfactorily without performance issues. I am very happy with the results however,.
This has been almost entirely implemented: in fact, with recent support for const generics, it means that a lot more abstraction can be done as a compile-time packed struct. Due to the limitations currently of const generics
, this means it's currently limited to a u128
, but we have a nice API for building this format at compile time, with every single method for the builder being a const fn
.
https://github.com/Alexhuszagh/rust-lexical-experimental/blob/main/lexical-util/src/format.rs
That’s the biggest hunk of conditionally generated docs i have yet seen in Rust, nice. And lovely progress!
Implemented as of lexical v6.0.0 and lexical-core v0.8.0. Cheers ^_^.