Search code examples
ruststaticborrow-checkerborrowing

Rust 'borrowed value does not live long enough' while assigning to a static variable


My goal is to keep a static variable and have the value be overridden by CLI arguments but I'm having a hard time finding a way to keep a static copy of the value I get from the args iterator.

static mut ROOT_DIRECTORY: &str = "C:\\test\\source";

fn main() {
  let args: Vec<String> = env::args().collect();
  let mut index = 0;
  for arg in args {
    match arg.as_str() {
      "/r" => unsafe {
        if args_length <= index + 1 {
          panic!("Missing root directory value.");
        }
        ROOT_DIRECTORY = args.get(index + 1).unwrap();
        if ROOT_DIRECTORY.is_empty() {
          panic!("Root directory value cannot be empty.")
        }
      }
    }
  }
}

Gives the following compilation error

error[E0597]: `args` does not live long enough
   --> src\main.rs:90:34
    |
90  |                 ROOT_DIRECTORY = args.get(index + 1).unwrap();
    |                                  ^^^^---------------
    |                                  |
    |                                  borrowed value does not live long enough
    |                                  argument requires that `args` is borrowed for `'static`
...
168 | }
    | - `args` dropped here while still borrowed

error[E0382]: borrow of moved value: `args`
  --> src\main.rs:90:34
   |
54 |     let args: Vec<String> = env::args().collect();
   |         ---- move occurs because `args` has type `std::vec::Vec<std::string::String>`, which does not implement the `Copy` trait
...
76 |     for arg in args {
   |                ----
   |                |
   |                value moved here
   |                help: consider borrowing to avoid moving into the for loop: `&args`
...
90 |                 ROOT_DIRECTORY = args.get(index + 1).unwrap();
   | 

Is there any way for me to create a static copy of the value from the iterator?


Solution

  • You cannot do that. Static variables must be 'static, that is, must not contain non-'static lifetimes. This is why you can elide lifetimes in the declaration of static references. Yours is actually equivalent to:

    static mut ROOT_DIRECTORY: &'static str = "C:\\test\\source";
    

    And your args is a local variable, so a reference to it is not 'static.

    Is there any way for me to create a static copy of the value from the iterator?

    The easiest option is to make the static variable own its data, instead of being a reference, that is, let it be a String. Unfortunately, the static constructor must be const, and the only const constructor of String that I know of is String::new(). You could add a helper function fn get_root_directory() -> &'static str that reads the global variable and returns the default if unset, but if you are into that, you could make the static a Option<String>:

    static mut ROOT_DIRECTORY: Option<String> = None;
    
    pub fn get_root_directory() -> &'static str {
        unsafe {
            ROOT_DIRECTORY.as_deref().unwrap_or("C:\\test\\source")
        }
    }
    

    Another option would be to leak a heap-allocated string to make it static. As long as you only assign to it once, the leak should not be a problem. Something like:

    static mut ROOT_DIRECTORY: &'static str = "default value";
    
    fn main() {
        let x = "...".to_string();
        unsafe {
            ROOT_DIRECTORY = Box::leak(x.into_boxed_str());
        }
    }