How to use trait with associated types as method parameter?

I am having trouble using a trait with an associated type as method parameter.

My code looks something like this:

///
/// A trait with an associated type, and a generic.
///
pub trait Foo<T> {
    /// Associated type that uses the specified generic.
    type GenericIt<'a>: Iterator<Item = T> where Self: 'a;

    /// A function that gives me the associated type.
    fn bar(&self) -> Self::GenericIt<'_>;
}

///
/// A function that processes a given trait.
///
#[allow(dead_code, clippy::disallowed_names)]
fn foo_bar<'a>(foo: &dyn Foo<i32, GenericIt<'a> = impl Iterator<Item = i32>>) {
    // Use the associated type.
    let _ = foo.bar();
}

I have the trait Foo on which I would call bar in foo_bar. This should give me an Iterator with items of type T, and the same life time as Foo, as I understand it.

foo_bar specifies GenericIt with the correct item type, and also assigns the required lifetime. However, this gives me the following error:

error[E0038]: the trait `Foo` cannot be made into an object
  --> tests/reproduce_error.rs:16:22
   |
16 | fn foo_bar<'a>(foo: &dyn Foo<i32, GenericIt<'a> = impl Iterator<Item = i32>>) {
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Foo` cannot be made into an object
   |
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
  --> tests/reproduce_error.rs:6:10
   |
4  | pub trait Foo<T> {
   |           --- this trait cannot be made into an object...
5  |     /// Associated type that uses the specified type.
6  |     type GenericIt<'a>: Iterator<Item = T> where Self: 'a;
   |          ^^^^^^^^^ ...because it contains the generic associated type `GenericIt`
   = help: consider moving `GenericIt` to another trait

I still want to use bar with the same lifetime as Foo, and I don't understand how I can do that. rust-analyzer suggests I move GenericIt out of Foo, but how would that work?

More information on why GATs are not supported in trait objects (yet): https://blog.rust-lang.org/2022/10/28/gats-stabilization.html#traits-with-gats-are-not-object-safe

1 Like

Depending on your use case, even if we did support object safe GATs, this might not fit your use-case. Trait objects for different types would still be different trait object types unless the iterators happen to be of the same type.

With a bit of run-time overhead due to more dynamic calls (and boxing), you could go for an approach of simply having bar() return Box<dyn Iterator<Item = T>> + '_.

If you have non-trait-object use cases, too, that shouldn’t suffer from the boxing, it’s also an option to define two traits, e.g. “Foo” and “FooDyn”, the latter with a bar_dyn method returning a Box<dyn Iterator<Item = T>> + '_, and adding a generic blanket impl for FooDyn on types that implement Foo. Feel free to ask for code examples if the suggestions weren’t detailed enough to be understandable.

1 Like