1

I want to define a trait that's a subtrait of std::ops::Index for a concrete reference type, e.g. something like this:

trait RefIndex<'a>: Index<&'a usize> {}

Which I can do, however, when I try to use it like:

fn get_sum<'a>(inp: impl RefIndex<'a, Output = u64>) -> u64 {
    let mut sum = 0;
    for i in 0..1 {
        sum += inp[&i];
    }
    sum
}

I get

5 | fn get_sum<'a>(inp: impl RefIndex<'a, Output = u64>) -> u64 {
  |            -- lifetime `'a` defined here
...
8 |         sum += inp[&i];
  |                    ^^
  |                    |
  |                    borrowed value does not live long enough
  |                    requires that `i` is borrowed for `'a`
9 |     }
  |     - `i` dropped here while still borrowed

I think this indicates that rust thinks the implementation of Index is only valid for references that live as long as get_sum when really I only want it for the transient lifetime of the function call. However, I'm not sure how to annotate RefIndex (or get_sum) in such a way to define that as the appropriate lifetime.

Note, for concrete types this problem doesn't happen, rust is able to infer the reduced lifetime of the reference and the following compiles just fine:

struct MyVec<T>(Vec<T>);

impl<T> Index<&usize> for MyVec<T> {
    type Output = T;

    fn index(&self, ind: &usize) -> &T {
        &self.0[*ind]
    }
}

fn get_sum_vec(inp: MyVec<u64>) -> u64 {
    let mut sum = 0;
    for i in 0..1 {
        sum += inp[&i];
    }
    sum
}
Erik
  • 6,470
  • 5
  • 36
  • 37
  • I believe this is because you make the caller choose `'a`, but you try to pass in a reference that only lives inside the loop- `&i` will become invalid once the loop body ends. – Filipe Rodrigues Dec 15 '21 at 17:21
  • 2
    Using `fn get_sum(inp: impl for<'a> RefIndex<'a, Output = u64>) -> u64` seems to fix your problem. See the [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c90060ea4002beb109ab34adaa7ed24a). Does this answer your question? Or must you keep the `'a` chosen by the caller? – Filipe Rodrigues Dec 15 '21 at 17:22
  • 1
    See [How does for<> syntax differ from a regular lifetime bound?](https://stackoverflow.com/questions/35592750/how-does-for-syntax-differ-from-a-regular-lifetime-bound) – Chayim Friedman Dec 15 '21 at 17:30

1 Answers1

1

Filipe Rodrigues answered it above:

RefIndex needs an explicit lifetime parameter, but declaring it as part of function scope unnecessarily bound it too broadly. By using the for<> syntax it can be bound more narrowly, which works.

fn get_sum(inp: impl for<'a> RefIndex<'a, Output = u64>) -> u64 {
    let mut sum = 0;
    for i in 0..1 {
        sum += inp[&i];
    }
    sum
}

I was not aware of the for syntax before now.

Erik
  • 6,470
  • 5
  • 36
  • 37