Correct lifetime for struct constructed from borrows to struct

Hi,

I’m trying to write a trait that builds a struct from borrows of the fields of the implmenter, e.g.

// pseudo code
// error handling omitted for brevity

trait Collector {
  type Key;
  type Collection: 'a; // would require to add 'a to trait signature 
                       // but lifetime depends on borrow instead of implementer

  fn collect<'a>(&'a self, key: &Self::Key) -> Self::Collection<'a>
}

struct Example {
  values: Vec<u32>
}

impl Collector for Example {
  type Key = (usize, usize);
  type Collection = (&'a u32, &'a u32); // Here the problem is obvious

  fn collect<'a>(&'a self, key: &Self::Key) -> Self::Collection<'a> {
    (&self.values[key.0], &self.values[key.1])
  }
}

As you can see my proble is that my assosiated type requires a lifetime but this lifetime originates from the method call not from trait implementation.
I tried a bit with higher-kinded-lifetimes (i.e. for<'a>) but I don’t realy figure out how to use these.

Hi, would that be good?

Well, that would be a solution but I have two problems with that:

  1. This adds a lifetime to the trait so my code gets spoiled with lifetimes everywhere.
  2. The trait’s lifetime expresses wrong semantics. If a trait has a lifetime-parameter it suggests that the implementer holds some reference, meaning that the lifetime is at least as long as the implementer’s. But the lifetime I want to express is only for the Collection I produce.

An example: When collect() is called the Collection must live as long as the Collector. This means I can no longer end the borrow of Collector by dropping the Collection because they have the same lifetime. As a result Collector can not be mutated after the first call of collect() even if the Collection is no longer in use!

Therefore, I want the lifetime tied to the Collection rather than the Collector.

BTW: I know this could be easily done with GATs but… well… we all know…

For the first issue, I can’t do anything but I don’t understand the second one.

Not necessarily, in your case it’s the opposite, it’s there to introduce a lifetime.

fn main() {
    let mut example = Example {
        values: vec![0, 1, 2],
    };
    let tuple = example.collect(&(0, 2));
    dbg!(tuple);
    example.values.push(5);
}

And as you can see, you can modify Example as soon as Collection is no longer used.

Ok, I see you’re right :sweat_smile:. My actual use case is way more complicated and I’m using values of the Collection as Keys for further querying of the Collector, seemingly I have some very specific case that can’t be reproduced by such a simple example.
Nevertheless, I solved the problem myself by removing the assosiate Collection. I had only two of them and now I have two Collector-traits either. This is not very nice but works until we have GATs…

What are GATs?

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