Rust lifetime in method definition

ClickUp
Note
AI Status
Last Edit By
Last edited time
Jul 28, 2023 02:59 PM
Metatag
Slug
rust-lifetime-method-definition
Writer
Published
Published
Date
Jul 28, 2023
Category
Rust
You have read the lifetime in Rust. Now you are ready to learn how the lifetime is implemented in a method definition.
If you have not read lifetime in Rust yet, you can go here.

Method Definition in Rust

Let’s write a method definition in Rust for learning reference down the road.
struct StringCompare { origin: &str, } impl StringCompare { fn longestString(&self, x: &str) -> &str { return self.origin; } }
In the above code, you have a StringCompare struct that has an origin of type &str and a method that accepts a &str parameter.
The longestString should compare origin and x length and return the longest string. But for now, you will just return the origin string to learn how lifetime inference works in method definition.

Lifetime inference when returning self.origin

First Rule Check: Assign different lifetime to each parameter

In this lifetime inference, the compiler will first assign a different lifetime to each reference parameter. &self will have its lifetime as written in StringCompare
struct StringCompare<'a> { origin: &'a str, } impl<'a> StringCompare<'a> { fn longestString<'b>(&self, x: &'b str) -> &str { return self.origin; } }

Third Rule Check: Assign &self lifetime to output lifetime

Because &self exists, the output will have the same lifetime.
impl<'a> StringCompare<'a> { fn longestString<'b>(&self, x: &'b str) -> &'a str { return self.origin; } }
Now every reference has a lifetime. There is no ambiguity, and the compiler can continue compiling your code.

Lifetime inference when return the longest string

Now you need to update the longestString implementation to compare origin with x and return the longest string.
impl<'a> StringCompare<'a> { fn longestString<'b>(&self, x: &'b str) -> &'a str { if self.origin.len() > x.len() { self.origin } else { x } } }
When you compile above code, you will find this error
5 | impl<'a> StringCompare<'a> { | -- lifetime `'a` defined here 6 | fn longestString<'b>(&'a self, x: &'b str) -> &'a str { | -- lifetime `'b` defined here ... 10 | x | ^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` | = help: consider adding the following bound: `'b: 'a`
The error is quite clear. It says “method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`" while pointing to the x variable in the else block.
This is because 'a and 'b have different lifetimes, and 'b probably will not live as long as 'a.
To fix the issue, you can assign 'a and remove 'b lifetime.
impl<'a> StringCompare<'a> { fn longestString(&self, x: &'a str) -> &'a str { if self.origin.len() > x.len() { self.origin } else { x } } }
That way, you tell the compiler that whatever reference is passed to x must live as long as our StringCompare instance.

Conclusion

Working with lifetime is tricky. There are pitfalls waiting around the corner. By understanding and following the three rules of compiler lifetime inference, you can always understand what error it reports. You can then solve that.
 

Discussion (0)

Related Posts