Assuming an API that cannot work properly with ZSTs, I can of course document that and assert/panic at runtime.
However, is there a trick (akin to what static_assertions
does - but that doesn't seem to work here) to assert this at compile time?
Assuming an API that cannot work properly with ZSTs, I can of course document that and assert/panic at runtime.
However, is there a trick (akin to what static_assertions
does - but that doesn't seem to work here) to assert this at compile time?
Something like this may work for you
trait PanicWhenZeroSized: Sized {
const _CONDITION: usize = (std::mem::size_of::<Self>() == 0) as usize;
const _CHECK: &'static str = ["type must not be zero-sized!"][Self::_CONDITION];
#[allow(path_statements, clippy::no_effect)]
fn assert_zero_size() {
<Self as PanicWhenZeroSized>::_CHECK;
}
}
impl<T> PanicWhenZeroSized for T {}
fn foo<T>() {
let _ = T::assert_zero_size();
}
fn main() {
foo::<u8>();
// foo::<()>();
}
Uncommenting the foo::<()>
line gives
Compiling playground v0.0.1 (/playground)
error: any use of this value will cause an error
--> src/main.rs:3:34
|
3 | const _CHECK: &'static str = ["type must not be zero-sized!"][Self::_CONDITION];
| -----------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
| |
| index out of bounds: the length is 1 but the index is 1
|
= note: `#[deny(const_err)]` on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
error: erroneous constant encountered
--> src/main.rs:6:9
|
6 | <Self as PanicWhenZeroSized>::_CHECK;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: could not compile `playground` due to 2 previous errors
Actually, apparently we can do panic
in const fn
now and it gives a better error message:
const fn check_non_zero_sized<T>() {
if std::mem::size_of::<T>() == 0 {
panic!("type is zero-sized");
}
}
trait PanicWhenZeroSized: Sized {
const _CHECK: () = check_non_zero_sized::<Self>();
#[allow(path_statements, clippy::no_effect)]
fn assert_zero_size() {
<Self as PanicWhenZeroSized>::_CHECK;
}
}
impl<T> PanicWhenZeroSized for T {}
fn foo<T>() {
let _ = T::assert_zero_size();
}
fn main() {
foo::<u8>();
foo::<[u8; 0]>();
}
Compiling playground v0.0.1 (/playground)
error[E0080]: evaluation of `<[u8; 0] as PanicWhenZeroSized>::_CHECK` failed
--> src/main.rs:3:9
|
3 | panic!("type is zero-sized");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| the evaluated program panicked at 'type is zero-sized', src/main.rs:3:9
| inside `check_non_zero_sized::<[u8; 0]>` at /rustc/2885c474823637ae69c5967327327a337aebedb2/library/core/src/panic.rs:57:9
...
8 | const _CHECK: () = check_non_zero_sized::<Self>();
| ------------------------------ inside `<[u8; 0] as PanicWhenZeroSized>::_CHECK` at src/main.rs:8:24
|
= note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain E0080`.
Note that neither version actually errors if the only use-case is in an unused private function.
Edit: Actually, this only works in the upcoming stable 1.57 (almost 2 weeks from now), or current beta or nightly.
With unstable incomplete features, you can also do something like
#![feature(generic_const_exprs)]
fn foo<T>()
where
[(); std::mem::size_of::<T>() - 1]:,
{
}
fn main() {
foo::<u8>();
foo::<()>();
}
or maybe something like
#![feature(generic_const_exprs)]
trait Assertion<const COND: bool> {}
impl Assertion<true> for () {}
fn foo<T>()
where
(): Assertion<{ std::mem::size_of::<T>() != 0 }>,
{
}
fn main() {
foo::<u8>();
foo::<()>();
}
Thanks a lot! Actually for the use case it should work on 1.48, so the first looks like what is needed
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.