Rust — common mistake 1 Link to heading
Consider the following code snippet.
struct Database {
// ...
}
struct Query<'a> {
db: &'a Database,
// ...
}
impl<'a> Query<'a> {
fn database(&self) -> &Database {
self.db
}
// ...
}
Do you see any issue above? The code compiles but there is an issue with the code.
To give you a hint, it is regarding the dreaded lifetime in Rust. To be more specific, what is the lifetime of reference to Database object returned by Query::database() method? Here is another clue: what happens when Database object outlives Query object?
Issue Link to heading
The issue here is that Query::database() method should return &Database type with the lifetime of Database object, but the code returns that with the lifetime of Query object instead, unnecessarily constraining the lifetime of the &Database. That is because lifetime elision rule states that
If there is exactly one input lifetime position (elided or not), that lifetime is assigned to all elided output lifetimes.
Hence, fn database(&self) -> &Database is equivalent to fn database<'s>(&'s self) -> &'s Database , meaning that the lifetime of &Database is the same as &self, i.e., query, and not db. Below code shows compilation error due to this issue.
#[derive(Debug)]
struct Database {
// ...
}
struct Query<'a> {
db: &'a Database,
// ...
}
impl<'a> Query<'a> {
fn database(&self) -> &Database {
self.db
}
// ...
}
fn main() {
let db = Database { /* ... */ };
// ...
let db_ref;
{
let query = Query { db: &db, /* ... */ };
db_ref = query.database();
}
println!("{:?}", db_ref);
}
error[E0597]: `query` does not live long enough
--> src/main.rs:25:14
|
24 | let query = Query { db: &db, /* ... */ };
| ----- binding `query` declared here
25 | db_ref = query.database();
| ^^^^^ borrowed value does not live long enough
26 | }
| - `query` dropped here while still borrowed
27 | println!("{:?}", db_ref);
| ------ borrow later used here
Solution Link to heading
The solution is to make sure return the lifetime of self.db rather than self by explicitly specifying lifetime of the returned type:
--- before
+++ after
@@ -9,7 +9,7 @@
}
impl<'a> Query<'a> {
- fn database(&self) -> &Database {
+ fn database(&self) -> &'a Database {
self.db
}
Note that 'a is the generic lifetime parameter of self.db, and therefore that is what should be returned. With this simple change, the code now compiles!
References Link to heading
The Rustonomicon The Dark Arts of Advanced and Unsafe Rust Programming doc.rust-lang.org
The Rust Reference Rust has rules that allow lifetimes to be elided in various places where the compiler can infer a sensible default… doc.rust-lang.org/reference/lifetime-elision.html
**