Understanding a lifetime error

I have a structure RolodexBase (used in the state structure for iced) which contains a HashMap (people) which maps usize to Person (another structure, owned by the RolodexBased). That has a method get_person to get a reference to a specific person by the usize key.

    |
121 |     pub fn get_person<'a>(&self, id: usize) -> Option<&'a Person> {
    |                       --  - let's call the lifetime of this reference `'1`
    |                       |
    |                       lifetime `'a` defined here
122 |         self.people.get(&id)
    |         ^^^^^^^^^^^^^^^^^^^^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`

Given that the HashMap and its Person(s) have the same lifetime as the RolodexBase, I thought I could use the same lifetime for the result. Apparently, I am missing something that should have been obvious to me. Sorry.
Joel

Might the signature of your function or method be a bit strange?

pub fn get_person<'a>(&self, id: usize) -> Option<&'a Person> {

For functions, the lifetime annotation of return values are typically related to one of the input reference parameters, and for methods there is typically no lifetime annotation after the method name, as in

// Function signature declaring and using explicit lifetime 'a
fn function_name<'a>(param: &'a str) -> &'a str { /* ... */ }

// Struct definition declaring a lifetime parameter 'a
// This indicates the struct holds a reference that must live at least as long as 'a.
struct StructName<'a> {
    // The field holds a reference to an i32 with lifetime 'a.
    field: &'a i32,
}

// Implementation block for a struct with lifetime 'a
// The lifetime must be declared again after 'impl'.
impl<'a> StructName<'a> {
    // Method signature using the struct's lifetime 'a.
    fn method_name(&self) -> &'a i32 { self.field }
}

Despite the name, Rust lifetimes like 'a are not about when values drop. They are about the duration of borrows.

This signature:

pub fn get_person<'a>(&self, id: usize) -> Option<&'a Person> {
// also spelled
pub fn get_person<'this, 'a>(&'this self, id: usize) -> Option<&'a Person> {

Means: "The caller can get a borrowed Person for any borrow duration they choose. The borrow of the Person has no relation to the borrow of Self. Uses of the return value do not keep the Self borrowed."

The caller could choose &'static Person for example. That would be bad :tm: as you could then drop the HashMap while the Person was still borrowed, etc.

Whereas this signature:

//                     vv-----------------------------vv
pub fn get_person<'a>(&'a self, id: usize) -> Option<&'a Person> {
// Also spelled
pub fn get_person(&self, id: usize) -> Option<&Person> {

Means: "The caller can get a borrowed Person for duration 'a if they supply a borrow of Self for the same duration 'a. Uses of the return value keep the Self borrowed."

That's what you need here. The return value keeping the Self borrowed prevents things like dropping the HashMap, etc.

4 Likes

Thanks That took care of it. THe markup I had used was what I thought the compiler had told me to do. I probably misunderstood the earlier error message.
Joel
PS: This string of questions comes from trying to unwind by use of RefCell / Rc.