There’s already #![feature(const_panic)]. Once that gets stabilized, the only other potential blocker would be const pattern matching (I don’t know if this is currently possible or not).
Cool! Pattern matching in const fn is already stable, so this actually gives a nice compilation message (with a nightly compiler), and changing the 0 to something non-zero gives a working program.
the compiler will catch zeros with an error at compile time.
error[E0080]: it is undefined behavior to use this value
--> src/lib.rs:4:1
|
4 | const FOO: NonZeroU8 = unsafe { NonZeroU8::new_unchecked(0) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected something greater or equal to 1
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
The second of these require allocation in a static context, so that is very far from what can currently be done. The first can be done without allocation, but having a const parse function would require a const FromStr trait, which would make allocation impossible in otherFromStr impls. So that is kind of impossible to. Unless there was to be a separate const_parse method and a separate ConstFromStr trait. Which may or may not be worth having in std, but could start of in a separate crate.
Addendum: One such separate crate seem to be https://crates.io/crates/const_env (it does its "parsing" by just dumping the value in the source, so even expressions work, including use of other const values in the program, which may be awessome or ... "surprising" ... depending on how you look at it).
use std::num::NonZeroU8;
macro_rules! unwrap {
($e:expr $(,)*) => {
match $e {
::core::option::Option::Some(x) => x,
::core::option::Option::None => {
["You tried to unwrap a None!"][10];
loop{}
},
}
};
}
pub const FOO: NonZeroU8 = unwrap!(NonZeroU8::new(0));
which produces this error
error: any use of this value will cause an error
--> src/lib.rs:8:17
|
8 | ["You tried to unwrap a None!"][10];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ index out of bounds: the length is 1 but the index is 10
...
15 | pub const FOO: NonZeroU8 = unwrap!(NonZeroU8::new(0));
| ------------------------------------------------------
| |
| in this macro invocation
|
= note: `#[deny(const_err)]` on by default
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
This works from Rust 1.46.0 onwards, which stabilized branching and looping in const contexts.
You can almost replace that macro with a const fn. Writing it specifically for Option<NonZeroU8> works:
const fn unwrap(opt: Option<NonZeroU8>) -> NonZeroU8 {
match opt {
Some(x) => x,
None => {
#[allow(unconditional_panic)]
["You tried to unwrap a None!"][10];
loop {}
}
}
}
But replacing the parameter with a generic makes the compiler complain about running destructors at compile time. This feels like a bug to me: the match always moves out all of the enum’s component values, so there’s nothing left to destruct at the end of the function.
Compiling playground v0.0.1 (/playground)
error[E0493]: destructors cannot be evaluated at compile-time
--> src/lib.rs:3:20
|
3 | const fn unwrap<T>(opt: Option<T>) -> T {
| ^^^ constant functions cannot evaluate destructors
...
12 | }
| - value is dropped here
It is perhaps worth noting that this doesn't catch some cases like enum using as on an repr, without const_panic, in cases it could in theory catch statically.