Hello.
I try to get a deeper understanding of the lifetime concept but i am a little confused with references explicit lifetime annotations. I got the concept, what they are needed for and how one should use them, still i am not totally sure about the semantics: What does the lifetime 'a
in a expression like fn as_str<'a>(data: &'a u32) -> &'a str
(an example from the rustonomicon) actually mean? (Example from the Rustonomicon) I got several explanations from different sources:
- The Borrow checker documentation insinuates to me, that
'a
actually is a name for the scope of the value, to which the reference foo is pointing. When an assignment of the returned reference is made e.g.x = as_str(data)
, it is checked, that the scope of x (distinct from its associated generic lifetime) is smaller than'a
(This seems to me to be the most likely explanation, still i want to verify my opinion, as the doc is rather lengthy and complicated for me) - The Rust Book 2nd edition suggests, that those lifetime parameters have no meaning for them alone, but just express relations between the lifetimes of references. The actual meaning is inferred from the stated constraints. Using the phrasing of the book i woulds translate declaration constraints into "the returned value and the input argument
data
will both live at least as long as'a
". To me, this is a little ambiguous, especially the "at least" part is confusing. From the additional explanations, i would understand, that the returned reference gets the lifetime'a
assigned, which is inferred from the input (´data´ in the example) lifetimes . It is still a little unclear, whether the "input lifteime" refers to the scope, where the given references are have been declared, or to the scope of the pointed-to values - The Nomicon ( doc.rust-lang .org/nomicon/lifetimes.html), as i read it, states, that the lifetimes of references are inferred by the borrow checker, as the minimum time, for which the borrowed value is used. In this case i am totally unclear how lifetimes are measured in this case (ins copes!?) and how this inference would work. (I get this understanding from the section which at the beginning, where 'desugared' examples are presented). I would see this as contradicting to variant 1
- A blog post (medium .com/@bugaevc/understanding-rust-ownership-borrowing-lifetimes-ff9ee9f79a9c) suggests, that "References, among other objects, have lifetimes too, and those can be different to the lifetime of the borrow they represent (called the associated lifetime). [...] A borrow may last longer than the reference it is controlled by". The distinction between the lifetime of the reference and
'a
as its associated lifetime makes sense to me. Still this post suggests, that the checker somehow infers a lifetime of a borrow. Especially, if a create a new reference from a previous one (e.g.let x=3; let newRef; { let oldRef = &x; let newRef = oldRef;} ...
), it tracks some kind of "borrow" object, which is the same for oldRef and newRef. This notion not too clear to me, especially for more complicated seetings. But despite of that, it also suggest, similarly to the Nomicon, that the "Rust compiler tries to make the borrow lifetime as short as possible" and infers a lifetime of the borrow (suggesting kind of a reverse checking by propagating back the liftime of the usage of the returned value to the input arguments and validating that they live long enough.
Therefore, my question: What is the actual concept? Maybe even a different one? I hope you understand my confusion from the stated explanation. It is not, that i do not get in principle, why explicit lifetimes need to be declared, and how to use them, but rather on understanding, how they work...
(P.S.: sorry for the blanks in the links, but as a noob i am sadly not allowed to use more than two links...)