Binding does not live long enough

I followed the suggested solution provided by the compiler but it resulted in the same error. I'm thinking that lazy_static is not a good use for global variables.

extern crate lazy_static;
#[macro_use]

use std::collections::HashMap;
use lazy_static::lazy_static;
use std::sync::Mutex;

#[derive(Debug)]
pub struct Service {
    url: String,
    used: i32,
}

#[derive(Debug)]
pub struct Protocol {
    name: String,
    services: Vec<Service>,
}

#[derive(Debug)]
struct Protocols {
    map: HashMap<String, Option<Protocol>>,
}

lazy_static! {
    static ref HASHMAP: Mutex<HashMap<String, &'static Option<Protocol>>> = {
        let m = HashMap::new();
        Mutex::new(m)
    };
}

fn main() {
    let mut map = HASHMAP.lock().unwrap();

    let p = Protocol {
        name: "One".to_string(),
        services: Vec::<Service>::new(),
    };
    let mut map = HASHMAP.lock().unwrap();

    let binding = Some(p);  // suggested by compiler. 
    map.insert("One3".to_string(), &binding);

    println!("{:?}", HASHMAP.lock().unwrap());
}

Make sure to check out Forum Code Formatting and Syntax Highlighting for future posts; I’m fixing your latest 2 topics for you, but it’s really quite hard to read code you share if you don’t wrap it in a code block properly.

In fact, you’ve actually received a pointer to the same formatting guide quite recently here:

1 Like

What is the motivation of wanting a reference in the HashMap? References in Rust (i.e. the types &T or &mut T for target types T) are meant for short-lived temporary references. If you need long-lived data with some shared-access indirection, you should look as things like Arc in std::sync - Rust.

In this particular example code, the need for any of this isn’t really apparent at all, so you could just make it an owned HashMap<String, Option<Protocol>>. Or use that Protocols type you’ve already defined. I must assume though that a lot of context is missing, because it isn’t clear at all why a global variable is needed here, either :wink:

The example code is just part of the application I am working on. The example is an experiment of building and accessing a series of related structures shared among a number of modules. Coming from a C++, Java, and Scala background I want to find the proper way (Rust-wise) to build and manipulate these structs.

I understand exactly what you mean and it makes sense that you'd try to do that, but it is very different in Rust. You can't use references for doing that. You start by using owned values, and then share by using smart pointers (Rc, Arc) or by using indexes/keys to access shared data that is stored in a collection. It is good you tried it, because now you can try it using these different approaches and get help with that here.


One thing that pops out is that these two definitions, if they are meant to be used for sharing Protocol objects, are incompatible.

    map: HashMap<String, Option<Protocol>>,
}

lazy_static! {
    static ref HASHMAP: Mutex<HashMap<String, &'static Option<Protocol>>> = {

The use of &'static Option<Protocol> won't work when Option<Protocol> is stored in a HashMap, in spite of that HashMap being stored in a static variable. I strongly recommend not using static variables, especially if you're trying to learn how Rust data is normally modeled.

The &'static reference means that the Option<Protocol> will always have the same location in memory. But storing it in a HashMap means that its location in memory can move, when the map is resized.

If you want to share at the granularity of Option<Protocol>, you can either:

  • Wrap that in an Arc and share that Arc<Option<Protocol>> by placing it in the map and in other places (Arc::clone it to increment its reference count for sharing), or
  • Outside the map, store the key to the entry containing the Option<Protocol>, and access the map using that key when you need it.

Unfortunately, when the compiler sees an incorrectly used reference, it makes some guess how to fix the reference, but it's not smart enough to tell you to just stop using temporary references because they're a wrong tool for the job.

This leads to the compiler incorrectly suggesting 'static references, instead of telling you to abandon the idea of using temporary references, and use Arc instead.

&'static is a special case that can only handle either constants compiled into the executable, or leaked memory. If you're not intentionally trying to do either of these, it's a dead-end.

All other references in Rust are temporary, intentionally anchored to a scope. &/&mut are not general-purpose pointers — they are strictly for borrowed data only.

They are inherently incapable of storing any data themselves, and exist to forbid keeping the data. They won't run any destructor, it's literally impossible for them to free memory of anything, they guarantee that. They also always have limited lifetime and scope, and it's impossible to ever extend that (you can't keep a reference beyond its statically pre-determined lifetime, it's guaranteed by the compiler).

So when you try to store data using a feature designed to forbid storing data, you will have your program fight itself.

When you try to use feature designed to restrict the scope where values can be used, and try to combine that with the maximally unrestricted global scope, the compiler will tell you to leak memory with 'static, because there's no other way to have a reference that guarantees the memory behind it won't be freed for as long as this (program global!) scope exists.

To store data, you need a type that can own memory. To store data "by reference", you still can't use Rust's references, and instead must use some other reference type, like Box (unique) or Arc (shared).

6 Likes