# Generic methods over object-safe traits

**URL:** <https://internals.rust-lang.org/t/generic-methods-over-object-safe-traits/6774>\
**Category:** language design\
**Created:** [February 16, 2018, 5:30pm UTC](https://internals.rust-lang.org/t/generic-methods-over-object-safe-traits/6774 "2018-02-16T17:30:02Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![dhardy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dhardy/32/2399_2.png) [@dhardy](https://internals.rust-lang.org/u/dhardy)\
**Post date:** [February 16, 2018, 5:30pm UTC](https://internals.rust-lang.org/t/generic-methods-over-object-safe-traits/6774/1 "2018-02-16T17:30:02Z")

</div>

We can add generic methods to otherwise object-safe traits by adding a `Sized` requirement:

```rust
trait T {
    fn spec(&self) -> u32;
    fn gen<X: From<u32>>(&self) -> X where Self: Sized {
        From::from(self.spec())
    }
}

```

but calling generic functions from type-erased trait objects is not possible:

```rust
// okay
fn f(t: &T) -> u32 { t.spec() }

// error: the `gen` method cannot be invoked on a trait object
// fn g_(t: &T) -> u32 { t.gen() }

```

This is quite understandable (generic functions cannot be included in a vtable), but we can get around it:

```rust
impl<'a> T for &'a T {
    fn spec(&self) -> u32 {
        (*self).spec()
    }
}

fn g(t: &T) -> u32 { (&t).gen() }

```

(also with `impl T for Box<T> { ... }`).

Why does the above work? Because the trait-object `&'a T` is a `Sized` object, and the above `impl` rule creates a vtable for it. But there’s one niggling issue: the need to use `(&t).gen()` instead of `t.gen()`.

Actually, we can get around that issue by making `gen` consume `self` instead of `&self`, but this causes `t.gen()` to consume `t`:

```rust
trait T {
    ...
    fn gen_consume<X: From<u32>>(self) -> X where Self: Sized {
        From::from(self.spec())
    }
}

fn h(t: &T) -> u32 { t.gen_consume() }

```

but calling `gen_consume` on a type which is not already a reference (or supports `Copy`) therefore consumes the object, unless manually referenced (e.g. `(&o).gen_consume()`).

This approach is workable, except perhaps for one thing: the error message on `g_` neither suggests the above `impl` rule, nor is affected by it, so there is no hint that users can fix it simply by replacing `t` with `(&t)`. Since many users find traits bewildering and may not understand how dynamic dispatch works I consider this a significant drawback — is it fixable?

[Link to the above on play.rust-lang.org](https://play.rust-lang.org/?gist=ece0c0a00d851c956b8692342231eeb9&version=stable)

There are some alternatives: [using an extension trait](https://github.com/rust-lang-nursery/rand/pull/244) separates the object-safe and non-object-safe parts of the trait, and thereby allows use of generic methods on the type-erased parent trait:

```rust
trait XT: T + Sized {
    fn gen<X: From<u32>>(&self) -> X {
        From::from(self.spec())
    }
    fn gen_consume<X: From<u32>>(self) -> X {
        From::from(self.spec())
    }
}
impl<TT: T + Sized> XT for TT {}

fn g(t: &T) -> u32 { t.gen() }

```

What’s more, this has reasonable error messages if the extension trait is not in scope:

```nohighlight
error[E0599]: no method named `gen` found for type `&T` in the current scope
  --> src/main.rs:29:29
   |
29 | fn g(t: &T) -> u32 { &t.gen() }
   | ^^^
   |
   = help: items from traits can only be used if the trait is in scope
   = note: the following trait is implemented but not in scope, perhaps add a `use` for it:
           candidate #1: `use XT;`

```

Serde [uses a similar trick](https://github.com/dtolnay/erased-serde#how-it-works) on type-erased trait-objects, mainly different in that the generic trait does not inherit the object-safe trait.

I should also mention [Huon Wilson’s blog post on allowing generic methods and object-safe traits](http://huonw.github.io/blog/2015/05/where-self-meets-sized-revisiting-object-safety/).

* * *

So why write all this?

1. These are tricky concepts to understand and it is not immediately obvious how or why the tricks work; hopefully this helps a few people understand.
2. To ask: is there a way to make `t.gen::<u32>()` work for `t: &T` without the extra trait?
3. Can the compiler at least suggest usage of `(&t).gen()`?

---

<div class="post-metadata">

**Author:** ![dhardy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dhardy/32/2399_2.png) [@dhardy](https://internals.rust-lang.org/u/dhardy)\
**Post date:** [February 16, 2018, 5:41pm UTC](https://internals.rust-lang.org/t/generic-methods-over-object-safe-traits/6774/2 "2018-02-16T17:41:53Z")

</div>

If we contrast the above with the boxed version:

```rust
impl T for Box<T> {
    fn spec(&self) -> u32 {
        (*self).spec()
    }
}

fn i(t: Box<T>) -> u32 { t.gen() }

```

we see that it’s not necessary to write `(&t).gen()` _even though_ `gen` is implemented in this case for `Box<T>` and therefore takes parameter of type `&Box<T>`. Why: because Rust has a special rule to automatically reference the object in a function call, used very commonly (e.g. in `o.spec()` the object `o` is implicitly referenced).

The exception is when the object is already referenced (as in `t.spec()`); unfortunately it appears this exception rule does not apply as desired when the function is implemented for _both_ `t` and `&t` but only one is actually usable, as in our example `t.gen()`.

---

<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:** [February 16, 2018, 7:30pm UTC](https://internals.rust-lang.org/t/generic-methods-over-object-safe-traits/6774/3 "2018-02-16T19:30:05Z")

</div>

@dhardy, `Box` version is giving me a stack overflow in the playground. The reference version does work.

I’m also wondering what that code is actually good for. Yes a generic method is invoked but it’s going to be the same method whatever the underlying type?..

---

<div class="post-metadata">

**Author:** ![dhardy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dhardy/32/2399_2.png) [@dhardy](https://internals.rust-lang.org/u/dhardy)\
**Post date:** [February 16, 2018, 8:42pm UTC](https://internals.rust-lang.org/t/generic-methods-over-object-safe-traits/6774/4 "2018-02-16T20:42:25Z")

</div>

Oh, sometimes useful generic methods can be built on top of non-generic ones, as in [`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) and [`Rng`](https://docs.rs/rand/0.4.2/rand/trait.Rng.html).

---

<div class="post-metadata">

**Author:** ![dhardy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dhardy/32/2399_2.png) [@dhardy](https://internals.rust-lang.org/u/dhardy)\
**Post date:** [February 16, 2018, 8:45pm UTC](https://internals.rust-lang.org/t/generic-methods-over-object-safe-traits/6774/5 "2018-02-16T20:45:57Z")

</div>

You’re right, I got the `Box` version wrong. This code appears to work:

```rust
impl T for Box<T> {
    fn spec(&self) -> u32 {
        (**self).spec()
    }
}

fn i(t: Box<T>) -> u32 { t.gen() }

    i(Box::new(o));

```

---

<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:** [February 17, 2018, 1:01am UTC](https://internals.rust-lang.org/t/generic-methods-over-object-safe-traits/6774/6 "2018-02-17T01:01:44Z")

</div>

> [@dhardy](#):
>
> Oh, sometimes useful generic methods can be built on top of non-generic ones

Right, but it does get rather [confusing](https://play.rust-lang.org/?gist=1d83fc6ad0b5cf858e58f5169ffc63d7&version=stable) doesn't it?

```rust
struct Foo;

impl T for Foo {
    fn spec(&self) -> u32 {42}
    fn gen<X: From<u32>>(&self) -> X where Self: Sized {
        From::from(11)
    }
}

```

Would it be less confusing if instead

```rust
fn gen<X: From<u32>>(t : &T) -> u32

```

was implemented as a stand-alone method outside of the trait?

---

<div class="post-metadata">

**Author:** ![dhardy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dhardy/32/2399_2.png) [@dhardy](https://internals.rust-lang.org/u/dhardy)\
**Post date:** [February 17, 2018, 11:13am UTC](https://internals.rust-lang.org/t/generic-methods-over-object-safe-traits/6774/7 "2018-02-17T11:13:46Z")

</div>

If you code it like that, yes. My opinion is that implementations have no business re-implementing `Iterator::last`, `Rng::gen` etc. — or in your example `Foo` should not implement `T::gen`.

Yes, `gen(&r)` is another possibility, but not functionally different than `use XT; (&r).gen()` and potentially less convenient (e.g. in the case of `Iterator` users would have to `use std::iter::last;` if that were a free function).

---

<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:** [March 25, 2019, 8:15am UTC](https://internals.rust-lang.org/t/generic-methods-over-object-safe-traits/6774/8 "2019-03-25T08:15:08Z")

</div>

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