Transitive Lifetimes

Hello fellow Rustaceans,

I am struggling to understand lifetimes, especially with the concept of transitive lifetimes. Despite watching all videos and reading many articles, I still encounter significant difficulties.

Specifically, I need help with a problem involving transitive lifetimes. Here's a summary of the traits I'm working with:

  1. Fields: This trait iterates over fields.
  2. Cells: This trait returns a list of cells for a row and is deducible from Fields.
  3. TableRows: This trait, also based on Fields, returns a list of rows, unlike Cells.

So, there is a transitive lifetime relationship: Fields -> Cells -> TableRows.

Could you please help me understand if this setup is correct and guide me through the lifetimes involved?

Certainly not; you are using way too many explicit lifetimes. That's a red flag in itself. In particular, you are putting lifetime parameters on a trait and then annotating the &self parameter with that lifetime – that's almost always nonsensical. If you want to express a relationship between lifetimes of the arguments and return type of a function, you shouldn't additionally constrain them with some other, external lifetime.

I'll try to fix this, but I can't guarantee anything will come out of it.


Edit 1: This compiles, but it still contains a ton of unnecessary cruft.


Edit 2: This removes all excess lifetime annotations.


Edit 3: This also removes unnecessary trait bounds.

3 Likes

Hello. Do you have an article or lecture in mind for me to read or watch?

then annotating the &self parameter with that lifetime – that's almost always nonsensical

So, that annotation of self was not redundant?

fn cells<'a>(&'a self)

It's preserved in edition 2.

That's magic. Thank you @paramagnetic :heart_decoration:

It was needed, but that wasn't my point anyway. The point is that it's not the lifetime parameter of the trait. It's declared directly on the method.

Sorry, no. You don't learn how to use lifetimes by watching lectures. You learn lifetimes in practice by writing a lot of code and thinking a lot.

3 Likes

I see

for<'a> Cow<'a, Cell>: From<<Row as Fields<CellKey>>::Value<'a>>,

How is that supposed to work? I used for<'a>, but that caused a problem in a in the outer code. It made the compiler think a temporary value was created when, in fact, it wasn’t. However, your suggestion might work; I will try it.

It the main trick using associated type

type Value<'v> where Self: 'v;

or that just nice to have thing?

Sorry, I don't get what you are trying to say. Please show actual code.

It's not the main trick, no, but it is pretty much a necessary component. The main problem was that you over-constrained the lifetimes. The associated type is necessary because a type parameter doesn't allow you to name the lifetime parameter in the specific way it is being used here.

1 Like

It's not the main trick, no, but it is pretty much a necessary component. The main problem was that you over-constrained the lifetimes. The associated type is necessary because a type parameter doesn't allow you to name the lifetime parameter in the specific way it is being used here.

I see. That is a problem with your solution. Both K and V need to be parameters, not associated types. Otherwise, the type for which Fields is implemented will be bound to a single type of V, but the same struct can have many implementations of Fields, not necessarily one as your implementation requires. I'm trying to rewrite your solution without using associated types.

You can introduce a dummy marker type parameter.

1 Like

Interesting :astonished: Is that only way?

I don't know. But it's certainly the simplest and only one I can think of.

2 Likes

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.