Description
While there are a smattering of useful const
functions in Jiff (like Date::constant
), the vast majority of all routines are not const
.
When I originally set out to build Jiff, I had a goal of making as much of the crate const
as possible. In theory, most APIs are "just math" and thus should be const
. The only things that at least conceptually cannot be const
in a datetime library are things like:
- Determining the current time.
- Determining the current time zone.
- Reading time zone data from a database at runtime.
But everything else can pretty much be const
. More than that, I believe const
in Rust is expressive enough where one could actually write a datetime library with that sort of support today (2024-03-10).
However, despite starting with this goal, I quickly gave it up. The specific thing that caused me to give up were Jiff's internal ranged integer types. They are built in a way where they track additional state and do additional checks whenever debug_assertions
is enabled. And crucially, they give half-way decent error messages when an invariant or boundary is broken. I found it incredibly difficult to make all of this work in const
. Some issues I ran into:
- While adding two
i32
values via the+
operator (and others) works inconst
Rust today, this is not supported for custom types. As far as I can tell, support for primitive operations like this is special cased inconst
Rust. But because traits can't be used inconst
Rust today, I'm not able to use operator overloading for ranged integer types. While this is technically a surmountable hurdle by simply declining to use operators, I found this to be far too annoying of a hurdle to clear. Moreover, I was unwilling to give up my specific formulation of ranged integers due to their incredible ability to provoke bugs in code that I never would have caught. - I found it very difficult to write nice panic error messages in
const
functions because none of the formatting machinery is available. This essentially means it is quite difficult to write a message that includes the troublesome value(s) and relevant boundaries. While this is also a surmountable hurdle by simply living with worse error messages, this was too big of a pill to swallow. It made diagnosing and tracking down bugs provoked by ranged integers too difficult. - While I mentioned this in the first bullet point, the overall inability to use traits in
const
Rust is a huuuuuge buzzkill. And this was especially damning in a ranged integer abstraction. In particular, ranged integers don't really want to care about their primitive representation. You want to be able to add anri8
with anri32
without thinking about casts or whatever. Indeed, that is their superpower. But of course, you still want to be able to specify a primitive representation so that types likeDate
are only 4 bytes. To make all of this work, you wantimpl Into<RangedIntegerTypeYouWant>
everywhere. This is simply incompatible withconst
Rust as it exists today.
And that's just for the ranged integers. I honestly don't know what other kind of issues I'd run into, but I never really got the chance to explore it. In particular, ranged integers are such a core and fundamental part of this library's internals, that if they can't be const
, then pretty much nothing can be. Indeed, even the existing const
routines like Date::constant
specifically need to duplicate code just to work in const
Rust.
This is overall something I'd like to support, but I think const
Rust needs to evolve considerably before it's compatible with the internal architecture of Jiff. In theory, Jiff could use something else that is more const
friendly internally, but the hurdle it will need to clear is huge. My best guess is that it will be years before this library sees a meaningful increase in the surface area of const
APIs.
I may be willing to consider one-off cases of making an API const
, but the trade off will almost certainly be something like, "duplicates code and gives up ranged integers." In other words, there probably needs to be a compelling use case for it to be const
. Something like Date::constant
is compelling because it's not uncommon to want to write dates in source code and treat them as constants. On top of that, these routines are limited to cases where there is no or very little code duplication and the operation is itself not too complicated.