Why can't I "manually instantiate" this generic function at a particular type?

It seems like if you have a working generic function, you ought to be able to manually substitute in a type and get a working less-generic function. But in this case it doesn't work.

Below, query_generic compiles fine. But when I manually substitute the specific type dyn U for T, the resulting less-generic function query_dyn does not compile. I don't understand the error message. What's going on here?

trait Table<K> {
    fn lookup<'view>(&'view self, key: &K) -> &'view Vec<String>;
}

trait U: Table<i32> + Table<u32> {}

fn query_generic<'view, A, T>(table: &'view T, key: &A) -> &'view Vec<String>
where
    T: Table<A> + ?Sized,
{
    <T as Table<A>>::lookup(table, key) // OK
}

fn query_dyn<'view, A>(table: &'view dyn U, key: &A) -> &'view Vec<String>
where
    dyn U: Table<A>,
{
    <dyn U as Table<A>>::lookup(table, key) // ERROR
}

Here's the full error message:

error[E0759]: `table` has lifetime `'view` but it needs to satisfy a `'static` lifetime requirement
  --> src/lib.rs:11:58
   |
7  | fn query_dyn<'view, A>(table: &'view dyn U, key: &A) -> &'view Vec<String>
   |                               ------------ this data with lifetime `'view`...
...
11 |     let f: fn(&'view dyn U, &A) -> &'view Vec<String> = <dyn U as Table<A>>::lookup;
   |                                                         -^^^^^--------------------- ...is captured and required to live as long as `'static` here

error[E0759]: `table` has lifetime `'view` but it needs to satisfy a `'static` lifetime requirement
  --> src/lib.rs:13:24
   |
7  | fn query_dyn<'view, A>(table: &'view dyn U, key: &A) -> &'view Vec<String>
   |                               ------------ this data with lifetime `'view`...
...
13 |     Table::<A>::lookup(table, key) // ERROR
   |                        ^^^^^ ...is captured here...
   |
note: ...and is required to live as long as `'static` here
  --> src/lib.rs:13:5
   |
13 |     Table::<A>::lookup(table, key) // ERROR
   |     ^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0759`.
error: could not compile `play-2021-08-17`

I think I found a clue. Both of these are fine:

fn query_dyn_static<'view, A>(table: &'view (dyn U + 'static), key: &A) -> &'view Vec<String>
where
    dyn U: Table<A>,
{
    <dyn U + 'static as Table<A>>::lookup(table, key) // OK
}

fn query_dyn_view<'view, A>(table: &'view dyn U, key: &A) -> &'view Vec<String>
where
    dyn U + 'view: Table<A>,
{
    <dyn U + 'view as Table<A>>::lookup(table, key) // OK
}

Hmm. I'm surprised dyn U isn't equivalent to one or the other of these.

Oh, maybe the issue is that

  • &'view dyn U implicitly means &'view dyn (U + 'view), but
  • dyn U: Table<A> implicitly means dyn U + 'static: Table<A>.

Then at least it makes sense that query_dyn is not a correct translation of query_generic. I'm still not sure why I'm getting this particular error message, though. It seems like Rust is using some lifetime variance to try to make it work.

This is lifetime elision I think. There's no such thing as dyn U, it is always dyn U + '_ for some lifetime. It's just that the lifetime is always elided: if dyn U is behind a reference, it's elided to the reference's lifetime, otherwise it is elided to 'static.

I don't think this is variance, it's a type mismatch error between dyn T + 'a and dyn T + 'static. Looks like the compiler tries to be helpful here and tries to explain why lifetimes are like this, which makes the error more confusing.

3 Likes

I believe this is exactly right (your bound doesn't match your input type). You could also use where for<'a> dyn U + 'a: Table<A> as the bound (assuming that makes sense in the larger context).

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.