# \[Idea\] Refining const generic types

**URL:** <https://internals.rust-lang.org/t/idea-refining-const-generic-types/12812>\
**Category:** language design\
**Created:** [July 30, 2020, 2:15am UTC](https://internals.rust-lang.org/t/idea-refining-const-generic-types/12812 "2020-07-30T02:15:39Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![CDirkx](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cdirkx/32/8514_2.png) [@CDirkx](https://internals.rust-lang.org/u/CDirkx)\
**Post date:** [July 30, 2020, 2:15am UTC](https://internals.rust-lang.org/t/idea-refining-const-generic-types/12812/1 "2020-07-30T02:15:39Z")

</div>

Thanks to the [great work](https://github.com/rust-lang/rust/issues/44580#issuecomment-662543117) of contributors, the implementation of const generics in Rust is getting further and further along.

As such, I myself have been experimenting on nightly, and considering some future extensions. One of the things I believe might be a future UX improvement with const generics, is a way to safely "refine" a generic const generic type to a specific type, as can be done today using an unsafe transmute:

```rust
#![feature(const_generics)]

struct S<const B: bool>;

impl S<true> {
  fn print_true(&self) { println!("true"); }
}

impl S<false> {
  fn print_false(&self) { println!("false"); }
}

impl<const B: bool> S<{B}> {
  fn print(&self) {
    match B {
      true => unsafe { core::mem::transmute::<&S<{B}>, &S<{true}>>(self).print_true() },
      false => unsafe { core::mem::transmute::<&S<{B}>, &S<{false}>>(self).print_false() }
    }
  }
}

```

[playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=f43a214e3ed75fd3bc12cb0b157c78d5)

I believe this to be sound (is it?).

Note that in this trivial example the implementation of `print` could be reduced to just

```rust
match B {
  true => println!("true"),
  false => println!("false")
}

```

but as a more complicated example, consider some trait implemented for specific type variant(s) but not the generic type:

```rust
trait T { fn print(&self); }
impl T for S<{true}> { ... }

impl<const B: bool> S<{B}> {
  fn print(&self) {
    match B {
      true => <S<{true}> as T>::print(self), // error: mismatched types; expected `&S<{true}>`, found `&S<{B}>`
      // or
      true => <S<{B}> as T>::print(self), // error: T is not implemented for S<{B}>, found implementation for S<{true}>

      false => ...
    }  
  }
}

```

I believe this conditionally "refining" a generic type `S<{B}>` to a specific variant `S<{true}>` is

- sound, when `B == true`
- potentially safe, I believe the compiler could have enough information to eliminate the need for unsafe transmutes
- useful, it allows you to use the traits, types and methods associated with a specific variant type when using the generic type, as shown in the example

Thoughts?

(I could not find if something like this has been discussed before, although the idea of bridging the gap between generic types and specific types seems similar to an earlier discussion, [Exhaustiveness checks for const generic impls](https://internals.rust-lang.org/t/idea-exhaustiveness-checks-for-const-generic-impls/10358): if a trait is implemented for `S<{true}>` and `S<{false}>`, should it automatically also be implemented for `S<{B}>`?)

---

<div class="post-metadata">

**Author:** ![H2CO3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/h2co3/32/2849_2.png) [@H2CO3](https://internals.rust-lang.org/u/H2CO3)\
**Post date:** [July 30, 2020, 8:27am UTC](https://internals.rust-lang.org/t/idea-refining-const-generic-types/12812/2 "2020-07-30T08:27:23Z")

</div>

This is basically downcasting but it doesn't (?) involve runtime type information. As such, I would consider it a big red flag. It might be useful as a last resort in some cases, but matching on the type and changing behavior based on that type is usually regarded as bad practice, as it is circumventing an abstraction boundary. As such, I strongly disagree that this should be added as a language feature.

If you depend on the `const` value parameter in a function, you should instead make the function itself generic, and match on the value in that generic function like so:

```rust
impl<const B: bool> S<{B}> {
    fn print_bool(&self) {
        println!("{}", B);
        // or, using the more explicit:
        match B {
            true => println!("true"),
            false => println!("false"),
        }
    }

    fn print(&self) {
        self.print_bool()
    }
}

```

---

<div class="post-metadata">

**Author:** ![bill\_myers](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bill_myers/32/4085_2.png) [@bill\_myers](https://internals.rust-lang.org/u/bill_myers)\
**Post date:** [July 30, 2020, 9:42am UTC](https://internals.rust-lang.org/t/idea-refining-const-generic-types/12812/3 "2020-07-30T09:42:04Z")

</div>

I agree that this should be added.

It is a standard feature in dependently typed languages, which wouldn't be usable without this.

It has nothing to do with "matching on types" directly, it's matching on values and refining a dependent type inside the match arm.

---

<div class="post-metadata">

**Author:** ![robinm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/robinm/32/6255_2.png) [@robinm](https://internals.rust-lang.org/u/robinm)\
**Post date:** [July 30, 2020, 10:34am UTC](https://internals.rust-lang.org/t/idea-refining-const-generic-types/12812/4 "2020-07-30T10:34:03Z")

</div>

Shouldn't this be expected to work?

```rust
trait T { fn print(&self); }
impl T for S<{true}> { ... }

impl<const B: bool> S<{B}> {
  fn print(&self) {
    match self {
      b @ S<{true}> => b.print(); // or more explicitely `<b as T>::print(b)`
      false => ...
    }  
  }
}

```

I would agree that the syntax isn't great since you can't use `self` as a binding on the left side of `@`, but for anything but `self` I think it would be totally fine.

---

<div class="post-metadata">

**Author:** ![steffahn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/steffahn/32/13288_2.png) [@steffahn](https://internals.rust-lang.org/u/steffahn)\
**Post date:** [July 30, 2020, 10:44am UTC](https://internals.rust-lang.org/t/idea-refining-const-generic-types/12812/5 "2020-07-30T10:44:45Z")

</div>

My take on this would be, this works today:

```rust
trait Print {
    fn print(&self);
}
impl<const B: bool> Print for S<B> {
    default fn print(&self) {
        unreachable!()
    }
}
impl Print for S<true> {
    fn print(&self) {
        self.print_true()
    }
}
impl Print for S<false> {
    fn print(&self) {
        self.print_false()
    }
}

fn call_print<const B: bool>(x: S<B>) {
    x.print()
}

fn main() {
    call_print(S::<true>);
    call_print(S::<false>);
}

```

(with specialization)

and this works today:

```rust
impl S<true> {
    fn print(&self) {
        self.print_true()
    }
}
impl S<false> {
    fn print(&self) {
        self.print_false()
    }
}

fn main() {
    S::<true>.print();
    S::<false>.print();
}

```

so then this _should_ work IMO:

```rust
impl S<true> {
    fn print(&self) {
        self.print_true()
    }
}
impl S<false> {
    fn print(&self) {
        self.print_false()
    }
}

fn call_print<const B: bool>(x: S<B>) {
    x.print()
}

fn main() {
    call_print(S::<true>);
    call_print(S::<false>);
}

```

i.e. the compiler should realize that there is an exhaustive set of `impl` blocks for boolean arguments, so calling `print` with a generic bool should be allowed.

---

<div class="post-metadata">

**Author:** ![Nemo157](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nemo157/32/11585_2.png) [@Nemo157](https://internals.rust-lang.org/u/Nemo157)\
**Post date:** [July 30, 2020, 12:01pm UTC](https://internals.rust-lang.org/t/idea-refining-const-generic-types/12812/6 "2020-07-30T12:01:36Z")

</div>

As mentioned this _is_ just a subset of (compile-time) downcasting (a.k.a. specialization), and can be safely implemented that way today:

```rust
#![feature(const_generics)]

use core::any::Any;

struct S<const B: bool>;

impl S<true> {
    fn print_true(&self) {
        println!("true");
    }
}

impl S<false> {
    fn print_false(&self) {
        println!("false");
    }
}

impl<const B: bool> S<{ B }> {
    fn print(&self) {
        if let Some(true_self) = (self as &dyn Any).downcast_ref::<S<true>>() {
            true_self.print_true();
        } else if let Some(false_self) = (self as &dyn Any).downcast_ref::<S<false>>() {
            false_self.print_false();
        } else {
            unreachable!();
        }
    }
}

fn main() {
    S::<true>.print();
}

```

_and_ this [gets successfully optimized into directly calling the correct implementation](https://rust.godbolt.org/z/68sjqo).

---

<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 30, 2020, 5:22pm UTC](https://internals.rust-lang.org/t/idea-refining-const-generic-types/12812/7 "2020-07-30T17:22:49Z")

</div>

This is effectively specialization, and should be treated as delicately as true specialization. However, in favor of "const specialization/refinement":

- referential transparency is already broken by `any::type_name`. This isn't carte blanche to freely break it everywhere else -- it's still a useful property and `any::type_name`'s purpose is to break it for debugging purposes -- but it is an argument of keeping it just to keep the property.
- extending that same argument, (minimal) specialization _is_ coming, eventually. In some limited fashion, trait impls will be allowed to refine behavior for specific types within a blanket impl.
- and unlike type information, which is black-boxed behind a (mostly) opaque type variable (or `dyn` indirection) and only available at runtime (modulo monomorphizations, which are really an implementation detail), the _point_ of `const` generics is to specialize behavior based on constant compile-time arguments.

It's that third argument that pushes me over the edge. (The first two are basically just weakening arguments against, the third is distinctly for.) `const` generics' _purpose_ is to be available at compile-time to make these kinds of dispatch decisions.

So _especially_ because it's already possible to optimize down to zero-cost with `Any`, I'd actually be behind some limited form of refinement typing should be _possible_, though I'd probably think matching on the (type of) the refined value rather than the const generic itself makes more sense within Rust's design (with explicit types that don't change to refine, because different types can have different representations), as sketched by @robinm

> [@robinm](#):
>
> ```rust
> trait T { fn print(&self); }
> impl T for S<{true}> { ... }
> 
> impl<const B: bool> S<{B}> {
> fn print(&self) {
> match self {
> b @ S<{true}> => b.print(); // or more explicitely `<b as T>::print(b)`
> false => ...
> }  
> }
> }
> 
> ```

---

<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 28, 2020, 5:22pm UTC](https://internals.rust-lang.org/t/idea-refining-const-generic-types/12812/8 "2020-10-28T17:22:49Z")

</div>

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