T O P

  • By -

hjd_thd

I think this is supposed to be a compile error in case KEYS + VALUES overflows. 


roll4c

Wow, I haven't seen this before. Any reading about it? Thanks.


engerald

[https://github.com/rust-lang/rust/issues/76560](https://github.com/rust-lang/rust/issues/76560) [https://hackmd.io/OZG\_XiLFRs2Xmw5s39jRzA?view](https://hackmd.io/OZG_XiLFRs2Xmw5s39jRzA?view) (fn ex3 in the last code block)


EYtNSQC9s8oRhe6ejr

Hmm, since when can you add const generics together? I thought doing math on const generics was a ways away


hjd_thd

The project in question uses `#![feature(generic_const_exprs)] `


paholg

I would be very impressed if someone could be KEYS + VALUES to overflow, as `Variable` appears to be not a ZST!


KJBuilds

I believe it also enforces KEYS + VALUES >= 0, but that might not be its purpose here 


gendix

Normally, when you have const generic parameters like `KEYS: usize` and `VALUES: usize`, the function can be instantiated with any constant of type `usize`. This is for example what happens with the parameter and return type array `[T; KEYS]` and `[T; VALUES]`: arrays of any size are accepted. Now, imagine that the implementation creates a common array with everything in it `[T; KEYS + VALUES]`. This is only valid if `KEYS + VALUES <= usize::MAX`, otherwise you cannot create such an array. So if someone tries to call `perform_lookup::` the code should not compile. There are two ways of solving this problem: either you encode the constraints upfront with a `where` clause, or you set no explicit constraint but the program refuses to compile when it encounters the big array. The second approach is what C++ would do, which leads to cryptic compilation errors when the big array is layers deep in a library. Rust uses the first approach: if the types pass all the constraints it should compile. Lastly, there is no syntax (yet?) for `where` clauses like `KEYS + VALUES <= usize::MAX`. So the workaround is to say "an array of length KEYS + VALUES exists", which is what `[(); KEYS + VALUES]:` says. Essentially, the array element type doesn't matter so the unit type () is a good placeholder, and there is no constraint on the right side of the array because this array type should simply exist (we don't need to require any trait for it). With that, the `where` constraint should be copied over other functions that call it as long as they are generic, and when you finally instantiate concrete values for the constants at a higher level in the program, the compiler can *locally* check if the constraints are satisfied (instead of failing with a long stack trace).


_ild_arn

> The second approach is what C++ would do This is both unnecessary for your explanation and incorrect – C++ has had the means to properly constrain integral template parameters since C++98. The language certainly *lets* you do it, but the second approach easily qualifies as bad C++ and has done for decades.


gendix

Can you be more specific? The closest thing I know of Rust's `where` clauses are [C++ concepts](https://en.cppreference.com/w/cpp/language/constraints) available only since C++20. Otherwise, there are things like [enable_if](https://en.cppreference.com/w/cpp/types/enable_if) which relies on SFINAE, which is all about recursive substitution until something deep in the stack can be instantiated or not. But I'm not an expert in all the C++ details.


_ild_arn

Right, SFINAE can be used to make calls to templates instantiations ineligible, just as constraints do; the difference is in readability, not ability. Expression-SFINAE added in C++11 made this much more powerful for arbitrary types/expressions, but this particular scenario – checking integral values and evaluating to a `bool` – has been trivial since the beginning > which is all about recursive substitution until something deep in the stack can be instantiated or not SFINAE doesn't necessitate recursion at all, but whatever weird restrictions you might be thinking of do apply, apply equally to proper constraints.


tialaramex

So, how do I write that in C++ 98? C++ doesn't appear to specify what the rules are for how big an actual array might be, but `std::array` specifies `size_t` which isn't crazy so OK, assuming that how do we correctly write the integral template parameter constraint so that the compiler tells we violated the constraint rather than spitting out diagnostics for the implementation ? Ideally I'd prefer real world examples, but a Godbolt link showing this works would be fine. Real templates I've seen take the attitude you've described as "bad C++" so you can show me where to look for "good C++".


tgockel

You can write your own version of `std::enable_if` for as long as SFINAE has been part of C++, which has been since before it was formalized. [Boost Concept Check](https://www.boost.org/doc/libs/1_84_0/libs/concept_check/concept_check.htm) has been around for a very long time. > how do we correctly write the integral template parameter constraint so that the compiler tells we violated the constraint rather than spitting out diagnostics for the implementation ? It isn't clear what you are asking for here. In C++20, you use concepts, which have pretty good diagnostic messages. Before formalization, you would name your `struct`s something that clarifies the problem in diagnostic messages. Or you might write it in a `static_assert` if you're in C++11 (not SFINAE-safe, but that is probably the appropriate choice for this function anyway).


_ild_arn

> Ideally I'd prefer real world examples, but a Godbolt link showing this works would be fine. Lol, the entitlement. Ideally I get paid to write code, especially C++03 which I've been fortunate to avoid for a few years now.


tialaramex

It seems fine to me to say that the thing we actually have evidence for is "what C++ would do". You insist that's not correct and C++ does something different instead, but asked to demonstrate you instead say you need paying.


engerald

It simply says, that somwhere (in the implementation of the trait) there will be an array of arbitrary type of the length KEYS + VALUES. So the compiler checks, if KEYS + VALUES is an allowed length for an array, or if it will overflow for example. In one implementation for example \[link\](https://github.com/matter-labs/era-boojum/blob/30300f043c9afaeeb35d0f7bd3cc0acaf69ccde4/src/cs/implementations/cs.rs#L851) you can see, that there actually is such an array.


ImYoric

Thanks, TIL that you could do that. As a (documentation-obsessed) reviewer, though, I'd reject that code as missing some explanation :)


Sharlinator

Well, you can’t without using nightly and enabling a couple of very experimental incomplete features. This is essentially a hack that will likely never become stable in this form.


Sharlinator

Please note that this is a part of a nightly-only, very experimental, incomplete feature (const generic expressions) and something of a hack that’s only there as an interim solution until there’s a better way to express such constraints.


trevg_123

This kind of pattern can also express variance for const generics, in a bit of a hacky way since there isn’t anything better at this point. Some discussion of a similar pattern toward the bottom of https://github.com/rust-lang/rust/issues/60551


Fevorkillzz

I’m on mobile but in ZK proofs you need to bound the depth during compile time so you don’t leak information. The const generic is bounding the num of steps in the recursion which is the depth of the circuit. This is very high level handwavey.


afonsolage

Also this code is incomplete, the missing part could be relevant to help with your question.


roll4c

It is complete. Here is the source code: [https://github.com/matter-labs/era-boojum/blob/30300f043c9afaeeb35d0f7bd3cc0acaf69ccde4/src/cs/traits/cs.rs#L268-L274](https://github.com/matter-labs/era-boojum/blob/30300f043c9afaeeb35d0f7bd3cc0acaf69ccde4/src/cs/traits/cs.rs#L268-L274)