How to use the Rustonomicon's way to interpret this returned lifetime issue

⚓ Rust    📅 2025-08-01    👤 surdeus    👁️ 11      

surdeus

Warning

This post was published 124 days ago. The information described in this article may have changed.

Consider this example:

use std::collections::HashMap;
fn get_default2<'r>(map: &'r mut HashMap<i32, ()>, key: i32) -> &'r mut () {
    if map.contains_key(&key) {
        // ^~~~~~~~~~~~~~~~~~ 'n
        return match map.get_mut(&key) {  // #1
            // + 'r
            Some(value) => value,   // |
            None => unreachable!(), // |
        }; // v
    }

    // At this point, `map.get_mut` was never
    // called! (As opposed to having been called,
    // but its result no longer being in use.)
    // #2
    map.insert(key, ()); // OK now.
    map.get_mut(&key).unwrap()
}

This code is sourced from 2094-nll - The Rust RFC Book. From the perspective of NLL, the example is ok because the Control-flow graph that goes through #1 won't go through #2, so the loan at #1 does not include the point #2.

However, I don't know how to interpret this example in terms of the Rustonomicon's way. I tried to annotate the example in Rustonomicon's manner:

fn get_default2<'r>(map: &'r mut HashMap<i32, ()>, key: i32) -> &'r mut () {
    'body:{
        if 't:{ map.contains_key(&'t key) } {
            return match map.get_mut(&'r key) {  // #1
                Some(value) => value,   
                None => unreachable!(),
            }; 
        }
        // #2
        map.insert(key, ()); // OK now.
        map.get_mut(&key).unwrap()
    }
}
/*
  'r:{
      get_default2(&'r map,0);
   }
*/

The lifetime 'r contains the function call, so the whole 'body is contained within 'r, so the borrowing borrowed at #1 should be alive at #2, which should conflict with the borrowing at #2.

Out-of-curiously, I wonder how to use the Rustonomicon's mental model to interpret this tricky problem.

4 posts - 3 participants

Read full topic

🏷️ Rust_feed