# Generic generics

**URL:** <https://internals.rust-lang.org/t/generic-generics/14080>\
**Category:** language design\
**Created:** [February 19, 2021, 3:12pm UTC](https://internals.rust-lang.org/t/generic-generics/14080 "2021-02-19T15:12:54Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [February 19, 2021, 3:12pm UTC](https://internals.rust-lang.org/t/generic-generics/14080/1 "2021-02-19T15:12:54Z")

</div>

Is it possible to have generic generics, to go with generic associated types?

Something like `struct Foo<T<'a>>`, used as `Foo<T<'a>=Bar<'a>>`.

---

<div class="post-metadata">

**Author:** ![Nokel81](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nokel81/32/3966_2.png) [@Nokel81](https://internals.rust-lang.org/u/Nokel81)\
**Post date:** [February 19, 2021, 3:20pm UTC](https://internals.rust-lang.org/t/generic-generics/14080/2 "2021-02-19T15:20:15Z")

</div>

What does `Foo<T<'a>=Bar<'a>>` mean in this context?

As for `struct Foo<T<'a>>` does this mean that `Foo` forces `T` to be generic over a lifetime parameter? What is the benefit of that over just defining what traits `T` should implement (if any)?

---

<div class="post-metadata">

**Author:** ![RustyYato](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/rustyyato/32/13627_2.png) [@RustyYato](https://internals.rust-lang.org/u/RustyYato)\
**Post date:** [February 19, 2021, 3:23pm UTC](https://internals.rust-lang.org/t/generic-generics/14080/3 "2021-02-19T15:23:35Z")

</div>

Rust doesn't support Higher Kinded Types, you can read about a hack to emulate them here

> **[Generalizing over Generics in Rust (Part 1) - AKA Higher Kinded Types in Rust](https://rustyyato.github.io/type/system,type/families/2021/02/15/Type-Families-1.html)**
>
> Do you want to use Higher Kinded Types (HKT) in Rust? Are you tired of waiting for GAT? Well, this is the place to be.

---

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [February 19, 2021, 3:24pm UTC](https://internals.rust-lang.org/t/generic-generics/14080/4 "2021-02-19T15:24:30Z")

</div>

It is setting the generic T\<'a\> to a Bar\<'a\>. Specifically, it needs to name the lifetime to use in the Bar\<'a\>.

The struct would carry with its type a type constructor. Ofc, you'd also need to have a PhantomData somewhere, like:

```rust
struct Foo<T<'a>> {
  _x: PhantomData<fn() -> T<'_>>
}

```

In particular you'd be able to do this:

```rust
impl<T<'a>> Something for Foo<T<'a>=T<'a>> {
  type ATC<'a> = T<'a>;
}

```

---

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [February 24, 2021, 12:50am UTC](https://internals.rust-lang.org/t/generic-generics/14080/5 "2021-02-24T00:50:09Z")

</div>

Here's something that cannot be done with GATs, but can be done with GGs: `AnyA<'a>`!

Example:

```rust
trait AnyA<'any_a> where Self: 'any_a {
  fn downcast_ref<T<'t_a>>(&self) -> &T<'any_a>;
}

struct Foo<'a>(&'a str);

fn bar<'bar_a>(x: &dyn AnyA<'bar_a>) -> Option<&'bar_a str> {
  x.downcast_ref::<T<'t_a>=Foo<'t_a>>().map(|foo| foo.0)
}

let s = String::from("hello world!");
let foo = Foo(&s);
let s_str = bar(&foo as &dyn AnyA<'_>);

```

---

<div class="post-metadata">

**Author:** ![RustyYato](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/rustyyato/32/13627_2.png) [@RustyYato](https://internals.rust-lang.org/u/RustyYato)\
**Post date:** [February 24, 2021, 2:27am UTC](https://internals.rust-lang.org/t/generic-generics/14080/6 "2021-02-24T02:27:45Z")

</div>

You can model that (even on stable!):

```rust
trait OneLifetimeParam<'a> {
    type This: 'a;
}

struct Foo<'a>(&'a str);
struct FooFamily;

impl<'a> OneLifetimeParam<'a> for FooFamily {
    type This = Foo<'a>;
}

trait AnyA<'any_a> where Self: 'any_a {
  fn downcast_ref<F: for<'t_a> OneLifetimeParam<'t_a>>(&self) -> &<F as OneLifetimeParam<'any_a>>::This;
}

impl<'a> AnyA<'a> for Foo<'a> {}

fn bar<'bar_a>(x: &dyn AnyA<'bar_a>) -> Option<&'bar_a str> {
  x.downcast_ref::<F=FooFamily>().map(|foo| foo.0)
}

fn main() {
    let s = String::from("hello world!");
    let foo = Foo(&s);
    let s_str = bar(&foo as &dyn AnyA<'_>);
}

```

However it won't compile because you can't have generic functions on a trait object

---

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [February 24, 2021, 2:55am UTC](https://internals.rust-lang.org/t/generic-generics/14080/7 "2021-02-24T02:55:19Z")

</div>

~~So wait, how does Any have generic functions again?~~

Oh, right, TypeId.

Uh. Model this on stable? ~~(This would require a bit more than just GGs, but...)~~

```rust
// ideally the commented "where" would be a thing, but if not we just mark the trait
// as unsafe and call it a day.
unsafe trait AnyA<'any_a> where Self: 'any_a /*where Self<'static>: 'static*/ {
  fn type_id(&self) -> TypeId;
}

// note that types such as &'static str are valid in T<'a> position, since 'a can just be ignored.
// so this would actually be implemented for *all* types with one or less lifetime parameters.
unsafe impl<'any_a, T<'a>> AnyA<'any_a> for T<'any_a> where T<'static>: 'static {
  fn type_id(&self) -> TypeId { TypeId::of::<T<'static>>() }
}

impl<'any_a> dyn AnyA<'any_a> {
  fn downcast_ref<T<'t_a>: AnyA<'t_a>>(&self) -> Option<&T<'any_a>> where T<'static>: 'static {
    if TypeId::of::<T<'static>>() == self.type_id() {
      // SAFETY: same as Any + because the lifetimes are literally encoded in the type system!
      unsafe { Some(&*(self as *const dyn AnyA<'t_a> as *const T<'t_a>)) } 
    } else {
      None
    }
  }
}

struct Foo<'a>(&'a str);

fn bar<'bar_a>(x: &dyn AnyA<'bar_a>) -> Option<&'bar_a str> {
  x.downcast_ref::<T<'t_a>=Foo<'t_a>>().map(|foo| foo.0)
}

let s = String::from("hello world!");
let foo = Foo(&s);
let s_str = bar(&foo as &dyn AnyA<'_>);

```

---

<div class="post-metadata">

**Author:** ![SkiFire13](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/skifire13/32/7579_2.png) [@SkiFire13](https://internals.rust-lang.org/u/SkiFire13)\
**Post date:** [February 24, 2021, 9:04am UTC](https://internals.rust-lang.org/t/generic-generics/14080/8 "2021-02-24T09:04:03Z")

</div>

How would you handle types that contain more than one lifetime and are invariant over them?

---

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [February 24, 2021, 12:56pm UTC](https://internals.rust-lang.org/t/generic-generics/14080/9 "2021-02-24T12:56:21Z")

</div>

What's invariant again?

Anyway, this handles zero or one lifetimes. To handle more you'd need AnyAB\<'a, 'b\>, AnyABC\<'a, 'b, 'c\>, or so on. For more lifetimes you'd have to somehow be able to have `unsafe trait AnyAB<'a, 'b> where Self: 'a + 'b` but if not, can always leave out the `where` and rely on the `unsafe`.

---

<div class="post-metadata">

**Author:** ![SkiFire13](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/skifire13/32/7579_2.png) [@SkiFire13](https://internals.rust-lang.org/u/SkiFire13)\
**Post date:** [February 24, 2021, 2:55pm UTC](https://internals.rust-lang.org/t/generic-generics/14080/10 "2021-02-24T14:55:26Z")

</div>

> [@Soni](#):
>
> What's invariant again?

See [`variance`](https://doc.rust-lang.org/nomicon/subtyping.html#variance). It basically means that if you have a `Foo<'short, 'long>` or `Foo<'long, 'short>` you can't convert them to a `Foo<'short, 'short>`, which would allow to use just one lifetime.

> [@Soni](#):
>
> Anyway, this handles zero or one lifetimes.

But you'll have to enforce this somehow, because otherwise you allow to swap the lifetimes of a type. Take for example `T<'a> = Foo<'a, 'static>` and `T<'a> = Foo<'static, 'a>`. They can both implement `AnyA<'a>` and the type id of `T<'static>` is the same between them, meaning you could transmute a `Foo<'a, 'static>` to a `Foo<'static, 'a>`.

---

<div class="post-metadata">

**Author:** ![RustyYato](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/rustyyato/32/13627_2.png) [@RustyYato](https://internals.rust-lang.org/u/RustyYato)\
**Post date:** [February 24, 2021, 3:21pm UTC](https://internals.rust-lang.org/t/generic-generics/14080/11 "2021-02-24T15:21:11Z")

</div>

No, you can only create a `TypeId` for `T<'static, 'static>`, so there is no safety concerns there

---

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [February 24, 2021, 3:34pm UTC](https://internals.rust-lang.org/t/generic-generics/14080/12 "2021-02-24T15:34:32Z")

</div>

Ah hm, so you'd need to relax TypeId::of to accept non-'static...

(Or otherwise disallow Foo\<'a, 'static\> from being a valid T\<'a\>... but Any does accept things like Cow\<'static, str\>, so it'd be inconsistent...)

---

<div class="post-metadata">

**Author:** ![notriddle](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/notriddle/32/14082_2.png) [@notriddle](https://internals.rust-lang.org/u/notriddle)\
**Post date:** [February 24, 2021, 4:37pm UTC](https://internals.rust-lang.org/t/generic-generics/14080/13 "2021-02-24T16:37:17Z")

</div>

> [@Soni](#):
>
> Ah hm, so you'd need to relax TypeId::of to accept non-'static...

No. Rust cannot support lifetime introspection. Higher-rank lifetimes on function pointers (you know, `for <'a> fn (&'a)`) allow you to use a function at a single memory address for whatever lifetime you want, so generating different code for different lifetimes isn’t an option. And any option that adds overhead for code that doesn’t use it is also a non-starter (so no hidden parameters). So it’s impossible to give types that differ only in lifetime different TypeIds.

It’s unsound to give them the same TypeId, because you’re supposed to be able to assume that types with the same TypeId can safely transmute between each other (that’s kind of what “being the same type” means), and it would allow dangling pointers if you did that with types that had different lifetimes.

---

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [February 24, 2021, 4:59pm UTC](https://internals.rust-lang.org/t/generic-generics/14080/14 "2021-02-24T16:59:50Z")

</div>

Rust can support lifetime-to-TypeId. Monomorphization applies to lifetimes as well, and different monomorphizations of the same lifetimes would be allowed to convert to different TypeId.

This means there could be some edge-cases where it would be safe to downcast a lifetime but the type system wouldn't allow you to because the monomorphization doesn't agree with it, but nevertheless it would still be sound. (Yes, that one with function pointers is a good example.)

---

<div class="post-metadata">

**Author:** ![RustyYato](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/rustyyato/32/13627_2.png) [@RustyYato](https://internals.rust-lang.org/u/RustyYato)\
**Post date:** [February 24, 2021, 5:10pm UTC](https://internals.rust-lang.org/t/generic-generics/14080/15 "2021-02-24T17:10:28Z")

</div>

> [@Soni](#):
>
> Monomorphization applies to lifetimes as well, and different monomorphizations of the same lifetimes would be allowed to convert to different TypeId.

This isn't true. Lifetimes are erased far before monomorphization occur. If lifetimes where monomorphized, it would result in a _massive_ hit to compile times and binary sizes.

---

<div class="post-metadata">

**Author:** ![notriddle](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/notriddle/32/14082_2.png) [@notriddle](https://internals.rust-lang.org/u/notriddle)\
**Post date:** [February 24, 2021, 5:34pm UTC](https://internals.rust-lang.org/t/generic-generics/14080/16 "2021-02-24T17:34:02Z")

</div>

> [@RustyYato](#):
>
> If lifetimes where monomorphized, it would result in a _massive_ hit to compile times and binary sizes.

It would also break C FFI. I can export a C function with lifetime parameters no problem, but exporting a C function with type parameters is a bit more of a problem:

```rust
// This will do what you mean
#[no_mangle]
extern "C" fn write_num_ref<'a>(c: &'a u32) {
    println!("{:?}", c);
}

// This won't
#[no_mangle]
extern "C" fn write_num_generic<T: Into<u32>>(c: T) {
    println!("{:?}", c.into());
}

fn main() {
    // call write_num_ref with two different lifetimes
    write_num_ref(&32u32);
    let u = 32u32;
    write_num_ref(&u);
    // call write_num_generic with two different types
    write_num_generic(32u32);
    write_num_generic(32u16); //~ERROR
}

```

[https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0702afe42c0659156b95289ac5bc2068](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0702afe42c0659156b95289ac5bc2068)

> [@Soni](#):
>
> This means there could be some edge-cases where it would be safe to downcast a lifetime but the type system wouldn't allow you to because the monomorphization doesn't agree with it, but nevertheless it would still be sound. (Yes, that one with function pointers is a good example.)

That's not actually what I'm worried about. My problem is with the fact that, right now, I can construct a single function pointer, and then, because of how subtyping works, use it as any lifetime. In retrospect, the FFI example is probably more compelling, because it's so explicit (and obviously unfixable, since it involves the C toolchain).

---

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [February 24, 2021, 6:04pm UTC](https://internals.rust-lang.org/t/generic-generics/14080/17 "2021-02-24T18:04:06Z")

</div>

Ah hm :‌(

You _could_ "tag" some lifetime parameters as monomorphizing (so that if you pass them to TypeId the whole thing needs to be monomorphized)... But yes, we see, and that makes us sad because we really wanted to see AnyA\<'a\> be a thing. Hm...

---

<div class="post-metadata">

**Author:** ![SkiFire13](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/skifire13/32/7579_2.png) [@SkiFire13](https://internals.rust-lang.org/u/SkiFire13)\
**Post date:** [February 24, 2021, 6:37pm UTC](https://internals.rust-lang.org/t/generic-generics/14080/18 "2021-02-24T18:37:50Z")

</div>

> [@RustyYato](#):
>
> This isn't true. Lifetimes are erased far before monomorphization occur. If lifetimes where monomorphized, it would result in a _massive_ hit to compile times and binary sizes.

I don't even think it's possible to monomorphize lifetimes since there might be infinitely many lifetimes (e.g. in a recursive function) and the exact number may be known only at runtime.

---

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [February 25, 2021, 4:43pm UTC](https://internals.rust-lang.org/t/generic-generics/14080/19 "2021-02-25T16:43:48Z")

</div>

What if you "cheated" with `TypeId::of::<for<'a> fn(PhantomData<T<'a>>)>()`? (thanks to @RustyYato for this one :‌p)

---

<div class="post-metadata">

**Author:** ![Soni](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@Soni](https://internals.rust-lang.org/u/Soni)\
**Post date:** [February 25, 2021, 5:14pm UTC](https://internals.rust-lang.org/t/generic-generics/14080/20 "2021-02-25T17:14:12Z")

</div>

```rust
unsafe trait AnyA<'any_a> where Self: 'any_a {
  fn type_id(&self) -> TypeId;
}

// note that types such as &'static str are valid in T<'a> position, since 'a can just be ignored.
// so this would actually be implemented for *all* types with one or less lifetime parameters.
unsafe impl<'any_a, T<'t_a>> AnyA<'any_a> for T<'any_a> where T<'any_a>: 'any_a {
  fn type_id(&self) -> TypeId { TypeId::of::<for<'a> fn(PhantomData<T<'a>>)>() }
}

impl<'any_a> dyn AnyA<'any_a> {
  fn downcast_ref<T<'t_a>: AnyA<'t_a>>(&self) -> Option<&T<'any_a>> where T<'static>: 'static {
    if TypeId::of::<for<'a> fn(PhantomData<T<'a>>)>() == self.type_id() {
      // SAFETY: caveats of Any apply, but in addition:
      //
      // - the concrete lifetimes don't actually matter for the TypeId, as we carry them throughout the code
      // (the dyn AnyA<'a> requires a lifetime parameter). we don't need them to be monomorphized as
      // such.
      //
      // - there are no issues related to lifetime bindings (e.g. T<'a>=Foo<'a, 'static> isn't the same as
      // T<'a>=Foo<'static, 'a> isn't the same as T<'a>=Foo<'static, 'static>) because we encode
      // these with an for<'a> fn(...<'a>) type. this basically allows us to get different TypeIds for
      // different ways to bind the same lifetime into a given type constructor.
      unsafe { Some(&*(self as *const dyn AnyA<'t_a> as *const T<'t_a>)) } 
    } else {
      None
    }
  }
}

struct Foo<'a>(&'a str);

fn bar<'bar_a>(x: &dyn AnyA<'bar_a>) -> Option<&'bar_a str> {
  x.downcast_ref::<T<'t_a>=Foo<'t_a>>().map(|foo| foo.0)
}

let s = String::from("hello world!");
let foo = Foo(&s);
let s_str = bar(&foo as &dyn AnyA<'_>);

```

[Next page](https://internals.rust-lang.org/t/generic-generics/14080.md?page=2)
