Beginner issue to understand lifetime


#1

I read offical doc about lifetime, and it’s unclear to me.

Code is here:

fn main() {
	let a = String::from("abcd");
	let b = "xyz";

	let c = longest(a.as_str(), b);
	println!("The longest string is {}", c);
}

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
	if x.len() > y.len() {
		x
	} else {
		y
	}
}

Since lifetime 'a is generic parameter, what is the exactly stand for during runtime, 'a is the lifetime of variable a or the lifetime of variable b. I guess the rule is the shortest one(so I guess is b), is my guess right? it’s not mentioned in doc.

And the return value’s lifetime must at least 'a, so the variable b lifetime?

If a function only has single parameter:

fn f<'a>(x: &'a str) -> &'a str {

}

that means the 'a is exactly argument’s lifetime, and the return value is at least the argument’s lifetime?


#2

The lifetimes are only used to statically analyse the code at compilation time to make sure there will not be any reference pointing to invalid data. There is no lifetime check during the runtime.

Basically, for your function above, you are telling the compiler that the reference returned by longest() cannot outlive the input parameters (x and y). So the variable used to store the reference returned by longest() must go out of scope before the variables used as input to longest().

The compiler would be well capable of finding out this kind of information by itself if your program is self contained (there are no external libraries involved). The reason the lifetimes must be specified in this scenario is mentioned in the book:

Rust would have to analyze every place the function is called to determine that there were no dangling references. That would be impossible because a library that you provide to someone else might be called in code that hasn’t been written yet, at the time that you’re compiling your library.


#3

however, the book said:

in

the book means the return reference lifetime scope >= 'a, however, you mean the return reference lifetime scope < 'a ?


#4

I think the book is saying pretty much the same thing that I try to but using different words.

The function will return a string slice that also will last at least as long as the lifetime 'a.

This means the reference returned by longest() must point to a String or &'static str which will live as long as any of the input arguments or longer, so that the reference points to valid data for its entire lifetime. But the variable used to store the returned reference itself cannot live longer then the input variables. E.g. this will not be allowed:

fn main() {
    let c;
	let a = String::from("abcd");
	let b = "xyz";

	c = longest(a.as_str(), b);
	println!("The longest string is {}", c);
}

edit: formatting


#5

Thank you very much. I’ll try to understand it.


#6

Follow your opinion, in this function call above, 'a is substituted to variable a or b lifetime?


#7

Neither, really. I think a better way to think of lifetime parameters is as constraints. A function like fn foo<'a>(x: &'a str, y: &'a str) -> &'a str is indicating a constraint: the returned reference lives at most as long as the shortest lifetime of x and y. When you call this function, the concrete lifetimes of x and y can be different. But, borrowck isn’t interested in concrete lifetimes (it fills those in itself, they’re anonymous), it’s interested in the relationship between that triple (x, y, and the returned reference).

You can also think of it as generic type constraints on a function: fn foo<T: Clone>(x: T). The T is anonymous, not known. But, you specified some constraint about it: it must implement Clone. The concrete type will be determined at the callsite. Here, you just express constraints.

Does that help?


#8

That helps me a lot. Thank you!