In what sense does it contain C? I would say that it “concretely” only contains the type C::Sub and has nothing to do with y and its lifetime parameter?
In Rust, lifetimes are used for temporary scope-limited references. This isn’t just any holding of an object by reference, it’s a very specific feature meant to make it absolutely impossible for the object to escape the scope it’s been borrowed from.
So to make it absolutely impossible to overlook that an object is always anchored to a scope and can’t leave it, this annotation is viral and impossible to get rid of. This way you can’t just stash an object inside a more complex type and smuggle it out of the restricted scope. When an object has a temporary reference, everything that may contain this object will also be just as temporary and have a lifetime attached.
If you did not intend to make the 'a always temporary and bound to a single scope, then you should replace the temporary loan & with some other reference type like Arc or Box that can be used more freely.
Roughly speaking what happens is, some function uses uses y to create some new values/objects that are owned and stored in D. Then afterwards C and y may go out of scope, but D still has to be used. Because the code is intended to be very “generic”, it seems ideal if D can inherit its types directly from C.
In a type system sense. A<C<'a>> contains a <C<'a> as SomeTrait>::Sub. In a context where you cannot normalize the Sub to something without 'a, the type is only valid where C<'a> (and thus 'a) is valid. This is important not only because the actual type may not be valid beyond 'a, but because of other aspects of the type system, like the implication that Sub: 'a. Forgetting lifetime parameters or their relationships can make the type system unsound, even if there's not an actual direct borrowing violation.[1]
Can you produce an example where the presence of the lifetime is actually a problem for you? There may be ways to work around it (in addition to the suggestions already made by others).
This seemed interesting but as fast as I can tell this doesn’t seem work, defining D like that and using it later in a concrete context forces C to be ‘static as well, even though it’s essentially irrelevant. (Edit: Oops this might be for other reasons, see the code linked later in this thread.)
Let’s say a function takes in something of type &Z as an input. It constructs something of type C, then later uses that (through &Z perhaps) to construct an instance of type D which only has owned fields, and wants to outputs the last instance.
Is this concrete enough? Naturally to redefine things (as perhaps in the approach of derspiny?) will work, I was just wondering if I’m missing something.
Not really, I meant something like a playground giving an error. The scenario you describe probably works fine where the types such as C<'_> are known.
Here's some guesses at how you may be able to effectively get rid of the lifetime in various scenarios anyway.
Thanks, this was encouraging. I think I’ve finally managed to isolate the issue (a bit unfortunate the compiler wasn’t more helpful here?):
So I think it’s because B in gogogo is implicitly given the same lifetime parameter as ch, which is not the intention. Is there a natural way to fix this? One fix would be to change fn example to
pub fn example(w: u64, ch: C<'_>) -> D<'static> { let d = gogogo::(ch, w); D{x: d.x} }