An impl Trait contained in a slice is typed differently?

trait Foo { }

fn set_element(
    slice: &mut [impl Foo], val: impl Foo, index: usize
) {
    slice[index] = val;
}

This does not compile; The error is as so:

error[E0308]: mismatched types
  --> src/main.rs:10:20
   |
8  |     slice: &mut [impl Foo], val: impl Foo, index: usize
   |                  --------        -------- found type parameter
   |                  |
   |                  expected type parameter
9  | ) {
10 |     slice[index] = val;
   |     ------------   ^^^ expected type parameter `impl Foo`, found a different type parameter `impl Foo`
   |     |
   |     expected due to the type of this binding
   |
   = note: expected type parameter `impl Foo` (type parameter `impl Foo`)
              found type parameter `impl Foo` (type parameter `impl Foo`)
   = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound
   = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

impl Trait, I heard that these expect "very specific types" but that they don't display them, or that those aformentioned "very specific types" go undefined in the code.

So I can't set the value of the element to val, because the slice contains a different type than that of val?

I've tried to allow this to work via type coercion too, and it still fails.

My use case was that I wanted a buffer of futures that I would poll, and then when any one future finished, I was going to filter in more by replacing finished values in the buffer with newly started futures.

My question is whether this is correct, that setting values in a slice of impl Trait is not something you can do? It feels like I have answered it myself, but I can't accept it, so here I am.

Correct. impl Trait cannot be used in this position. You can use dyn Trait instead.

1 Like

Every impl Trait in argument position[1] is a new generic. So your function is akin to this:[2]

fn set_element<T: Foo, U: Foo>(slice: &mut [T], val: U, index: usize) {
    slice[index] = val;
}

You want this (AFAICT, but see also near the end of this comment).

fn set_element<T: Foo>(slice: &mut [T], val: T, index: usize) {
    slice[index] = val;
}

That sounds like impl Trait everywhere else except argument position, in particular when in return position:[3]

// This `impl Display` is an opaque alias of the `String` type specifically
fn foo() -> impl Display {
    "hi".to_string()
}

But even there, different impl Trait are distinct types.

// This compiles even though there are two different types
// being opaquely aliased
fn bar() -> (impl Display, impl Display) {
    ("Hello".to_string(), 0)
}

// And even when they *happen* to alias the same type...
fn quz() -> (impl Display, impl Display) {
    ("Hello".to_string(), "world".to_string())
}

// ...callers aren't allowed to know that!  Because having the
// flexibility for `quz` to change the types is part of the point
// of making them opaque.
fn main() {
    let (mut a, b) = quz();
    a = b; // Type mismatch error
}

(Playground.)


Then you probably do need some sort of type erasure, as like closures, futures tend to have unique unnameable types. So you might end up with a Pin<Box<dyn Send + Future<Output = ...>>> or something.

This last part is just a heads-up on something you might run into. Going back to your minimized code...

trait Foo { }

// This works
fn set_element_boxed(slice: &mut [Box<dyn Foo>], val: Box<dyn Foo>, index: usize) {
    slice[index] = val;
}

// This still works
fn set_element<T: Foo>(slice: &mut [T], val: T, index: usize) {
    slice[index] = val;
}

// But this doesn't work (yet)
fn example(mut vec: Vec<Box<dyn Foo>>, val: Box<dyn Foo>) {
    set_element(&mut vec, val, 0);
}

dyn Foo implements Foo automatically, but Box<dyn Foo> and other pointers do not. However, you can provide your own implementation.

// todo: `Box<dyn Foo + Send + '_>` and a lot of others... (macro time)
impl Foo for Box<dyn Foo + '_> {}

// Now this works
fn example(mut vec: Vec<Box<dyn Foo>>, val: Box<dyn Foo>) {
    set_element(&mut vec, val, 0);
}

(Playground.)

For futures specifically, Pin<Box<..>> already has the necessary implementation so this won't be a problem if you use that. (It's something you have to deal with for your own traits, and hope upstream dealt with for their traits.)


  1. "APIT" ↩︎

  2. with some downsides due to the types not being nameable ↩︎

  3. "RPIT" ↩︎

4 Likes

each impl Trait in the parameter list is a distinct type, so your function signature is equivalent to:

fn set_element<T1: Foo, T2: Foo>(
    slice: &mut [T1], val: T2, index: usize
);
1 Like

Okay, thank you very much.
Being able to let go of this, I can see another use-case for what I was trying to do; Involves spawning threads instead of storing futures in a buffer.