I tried a bunch of different ways based on reading snippets, and I haven't found any that compile. Interestingly rustc doesn't even suggest any feature flags for this which makes me wonder if what I'm trying to do is impossible even with nightly?
Playground link. In the past I did get this kind of check working once with typenum, but the compiler errors were worse than the ones I'd get in C++ so I don't think that's a practical route esp. if I have to support this code being used by others.
This can be adapted to pretty much any constant-evaluated condition. However, a new type must be used for every kind of condition, since no operations can be done on the const generic parameters themselves.
Not sure I understand. It looks like you are doing operations on generic parameters? I think you mean you need a new type each time because if you put multiple associated consts for different conditions in the same impl they would all get triggered at once when the impl is instantiated?
What I mean is that the actual arithmetic and comparisons must be done in the value of the associated constant. The type parameters to Assert must pass through the values directly. So we can't write, e.g., struct Assert<const COND: bool>; with const OK: () = assert!(COND); and test Assert::<{ N % 4 == 0 }>::OK, at least not without generic_const_exprs. As a collorary, if we want to test several different conditions, we need several different Assert types, each with a condition in its OK constant.
With nightly you have @vague's answer, which is the proper solution.
On stable, you can use post-monomorphization errors (panic!s when resolving generic constants) to emulate that, but it's really a poorman's substitute, since cargo check won't detect those failures, only the final cargo build can, and it will only do so if such a function is deemed reachable enough for it to make it to codegen (thence triggering a monomorphization of that generic const, and thus, observing the panic). This is what @LegionMammal978's solution is about, but it was missing these obligatory disclaimers about the limitations of post-mono errors
In other words: if you're compiling the final binary, an approach based on post-mono checks is fine. But if you're just a library, then you may make mistakes which you'll never detect and only a downstream user will, which will be highly inconvenient for them. So I would advise against post-mono checks for library authors.
On the other hand, @LegionMammal978's approach has the advantage of using a compile-time panic for a way nicer error message. So we can take that idea and apply it to the nightly approach, yielding:
so that a test2::<3>, even in unreachable code, yields:
error[E0080]: evaluation of `AssertDivisibleBy4::<3_u32>::OK` failed
--> src/lib.rs:8:9
|
8 | assert!(N % 4 == 0, "must be divisible by 4");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'must be divisible by 4', src/lib.rs:8:9
|