# Idea: trait bounds on constants?

**URL:** <https://internals.rust-lang.org/t/idea-trait-bounds-on-constants/14998>\
**Category:** language design\
**Created:** [July 11, 2021, 1:18am UTC](https://internals.rust-lang.org/t/idea-trait-bounds-on-constants/14998 "2021-07-11T01:18:33Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![newpavlov](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/newpavlov/32/3290_2.png) [@newpavlov](https://internals.rust-lang.org/u/newpavlov)\
**Post date:** [July 11, 2021, 1:18am UTC](https://internals.rust-lang.org/t/idea-trait-bounds-on-constants/14998/1 "2021-07-11T01:18:33Z")

</div>

In RustCrypto we have a [generic implementation](https://docs.rs/ccm/0.4.3/ccm/struct.Ccm.html) of the [CCM](https://en.wikipedia.org/wiki/CCM_mode) mode. This mode is generic over two integer parameters (tag and nonce sizes) and the algorithm is defined only for a number of them. With `typenum` which we currently use defining this restriction is easy enough, we simply introduce (sealed) traits implemented only for valid sizes. But what should we do with a hypothetical migration to const generics?

One way is to abuse the proposed [`const_evaluatable_checked` syntax](https://github.com/rust-lang/rust/issues/76560):

```rust
struct Ccm<C, const N: usize, const M: usize>
where
    C: BlockCipher,
    [(); is_valid_nonce_size(N)]: Sized,
    [(), is_valid_tag_size(M)]: Sized,
{ .. }

const fn is_valid_nonce_size(n: usize) -> usize {
    match n {
        7..=13 => 0,
        _ => panic!("invalid CCM nonce size"),
    }
}

```

The code can be improved a bit by introduction of hypothetical `require` clauses:

```rust
struct Ccm<C: BlockCipher, const N: usize, const M: usize>
where C: BlockCipher
require is_valid_nonce_size(N), is_valid_tag_size(M)
{ .. }

const fn is_valid_nonce_size(n: usize) -> bool { ... }

```

But it still feels quite weird to restrict space of valid constant parameters using `const fn`s. We effectively use imperative approach instead of the usual declarative one. Also this approach does not allow extension of allowed const parameters in third-party crates (we don't need it in the CCM's case, but it could be really useful in some use cases).

Const parameters behave as types in many regards, so how about allowing trait bounds on const parameters?

```rust
struct Ccm<C: BlockCipher, const N: usize, const M: usize>
where C: BlockCipher, N: NonceSize, M: TagSize
{ .. }

trait NonceSize {
    // potential helper methods tied to a constant.
    // constants will not be able to implement traits with
    // methods containing `self` parameters.
    fn foo();
}

impl NonceSize for const 4usize { .. }

```

Of course, this method is only practical when number of accepted values is relatively small.

Unresolved questions:

- Should we distinguish between general traits and those intended for being implemented by constants in their definition?
- Calling syntax for trait methods on constants.

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [July 11, 2021, 2:47am UTC](https://internals.rust-lang.org/t/idea-trait-bounds-on-constants/14998/2 "2021-07-11T02:47:41Z")

</div>

`nalgebra` has an interesting approach / prior art here, in that they have roughly the equivalent of

```rust
struct Ccm<C, const N: usize, const M: usize>
where
    C: BlockCipher,
    Const<N>: NonceSize,
    Const<M>: NonceSize,
{ ... }

trait NonceSize { .. }
struct Const<const N: usize> { .. }

impl NonceSize for Const<4> { .. } 

```

So this shows that it's possible already to "lift" const parameters into type parameters that can implement traits, etc.

In the face of that, I don't think actually allowing trait impls for const values is _too_ far out there.

That said, I'd need to see where const bounds in general go to know if using that system would be a better approach. `const_evaluable_checked` as is is (AIUI) just a stopgap working solution while we work on a better solution.

---

<div class="post-metadata">

**Author:** ![anon32976453](https://avatars.discourse-cdn.com/v4/letter/a/49beb7/32.png) [@anon32976453](https://internals.rust-lang.org/u/anon32976453)\
**Post date:** [July 11, 2021, 3:21am UTC](https://internals.rust-lang.org/t/idea-trait-bounds-on-constants/14998/3 "2021-07-11T03:21:56Z")

</div>

The motivation is compelling and I wholeheartedly agree we should prefer the declarative approach.

However I think a better solution that doesn't extend traits to values (edge cases and high complexity imo) is to go back to old school ranges. I'm taking about Pascal/ADA style range types, not to be confused with what we have in rust.

I'll leave the syntax discussion/bikeshedding for now but the idea and semantics are as follows - allow the user to define a new (sub-)type of integers. It could be a mathematical range or set of values. You could then use that type in the usual manner, such as implement traits for it and whatnot.

Straw man example:

```
type MyValues : u8 = {2, 4, 6, 10..=20};

```

Of course, there are a couple of subtleties with this. We'd probably need to carefully define sub-typing relationships and allowed operations on these.

Edit: As @CAD97 said, uplifting values into types. And if it can be done in library without additional syntax even better!

---

<div class="post-metadata">

**Author:** ![programmerjake](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/programmerjake/32/5893_2.png) [@programmerjake](https://internals.rust-lang.org/u/programmerjake)\
**Post date:** [July 11, 2021, 10:04pm UTC](https://internals.rust-lang.org/t/idea-trait-bounds-on-constants/14998/4 "2021-07-11T22:04:33Z")

</div>

I proposed const generics actually just being syntax sugar for types on [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/custom.20literals.20using.20const.20generics/near/243991484):

> hmm, what if `<const N: usize>` was just syntax sugar for `<N: core::const::Const<usize>>`?

---

<div class="post-metadata">

**Author:** ![anon32976453](https://avatars.discourse-cdn.com/v4/letter/a/49beb7/32.png) [@anon32976453](https://internals.rust-lang.org/u/anon32976453)\
**Post date:** [July 12, 2021, 10:00am UTC](https://internals.rust-lang.org/t/idea-trait-bounds-on-constants/14998/5 "2021-07-12T10:00:29Z")

</div>

I don't think that's quite right. By uplifting to types I meant that we ought to keep with declarative constraints over types (and not values) and the set of values should be defined by a type. As is normal practice.

In other words, Rust's design should strive to adhere to SRP: we shouldn't need to do procedural computation over types and we equally shouldn't need to use complex declarative constraints over values. In other words we shouldn't mix the two domains if possible.

The above example wants to define a set of valid nonce size values, yeah? Well, That's the role of a type. It should be:

```
struct CCM<C: BlockCipher, const N: NonceSize, const M: NonceSize> {...}

```

With a type defined somehow like in my previous post:

```
type NonceSize : usize = {2, 4, 6}; // whatever is appropriate here

```

This is still work in progress though and not even completed on nightly.

---

<div class="post-metadata">

**Author:** ![anon32976453](https://avatars.discourse-cdn.com/v4/letter/a/49beb7/32.png) [@anon32976453](https://internals.rust-lang.org/u/anon32976453)\
**Post date:** [July 12, 2021, 10:31am UTC](https://internals.rust-lang.org/t/idea-trait-bounds-on-constants/14998/6 "2021-07-12T10:31:00Z")

</div>

Rust already has unsafe union types, used for FFI with C. They could have an additional safe form for the above case.

```
union NonceSize : usize {... List of values... }

```

Unlike the existing unsafe union which lists fields, this form lists values which must belong to the parent type (usize in this case). This is safe cause all the values have the same memory size.

You could have a runtime value of this type and of course a const value:

```
let x: NonceSize = ... ;
const y: NonceSize = ... ;
```

---

<div class="post-metadata">

**Author:** ![atagunov](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/atagunov/32/5877_2.png) [@atagunov](https://internals.rust-lang.org/u/atagunov)\
**Post date:** [July 12, 2021, 12:52pm UTC](https://internals.rust-lang.org/t/idea-trait-bounds-on-constants/14998/7 "2021-07-12T12:52:51Z")

</div>

> [@anon32976453](#):
>
> `union NonceSize : usize {... List of values... }`

Does that not look more like an `enum` rather than a `union`?

---

<div class="post-metadata">

**Author:** ![newpavlov](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/newpavlov/32/3290_2.png) [@newpavlov](https://internals.rust-lang.org/u/newpavlov)\
**Post date:** [July 12, 2021, 10:58pm UTC](https://internals.rust-lang.org/t/idea-trait-bounds-on-constants/14998/8 "2021-07-12T22:58:28Z")

</div>

I agree it would be nice to have ergonomic type-level ranges and sets. With them the CCM example could look like this:

```rust
// strawman syntax
type NonceSizes = Range<7usize..=13>;
// it could be worth to restrict set values to a single type
type TagSizes = Set<4usize, 6, 8, 9, 10, 12, 14>;
struct Ccm<C: BlockCipher, const N: NonceSizes, const M: TagSizes> { .. }

// for ergonomics it's worth to allow automatic conversion of
// { integer } to ranges and sets
type CcmInstance = Ccm<Aes128, 7, 4>;

```

But this approach does not solve the use case with potential extensibility in third-party crates. Although I am not sure if it's important enough or not.

---

<div class="post-metadata">

**Author:** ![anon32976453](https://avatars.discourse-cdn.com/v4/letter/a/49beb7/32.png) [@anon32976453](https://internals.rust-lang.org/u/anon32976453)\
**Post date:** [July 13, 2021, 12:39am UTC](https://internals.rust-lang.org/t/idea-trait-bounds-on-constants/14998/9 "2021-07-13T00:39:49Z")

</div>

> [@atagunov](#):
>
> Does that not look more like an `enum` rather than a `union` ?

It seemed to me that since `enum` is a discriminated sum type it is a bit less suitable. The set was supposed to contain untagged values.

> [@newpavlov](#):
>
> I agree it would be nice to have ergonomic type-level ranges and sets.

There's already a POC crate for a ranged type published. The Set type is more tricky to implement in a library since we don't have variadic genetics yet.

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [October 11, 2021, 12:40am UTC](https://internals.rust-lang.org/t/idea-trait-bounds-on-constants/14998/10 "2021-10-11T00:40:23Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
