2

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?

Paul
  • 42
  • 5
  • 19
  • What is passed in the args list cannot exist in static memory; static memory is for data that exists for the duration of the program execution, for data that is part of the program binary. Are you able to tell us what you are trying to do rather than what you are trying to code? – JustKash Mar 07 '20 at 19:05
  • That makes no sense. Static memory is for the lifetime of the process and for sure it can mutate during the execution of the application. Why not be able to promote a piece of memory to not be free? – Paul Mar 07 '20 at 19:10
  • Sorry, my point was not that static memory cannot be changed, it can be unsafely changed in Rust. But arg or args doesn't have a static lifetime and therefore does not guarantee existence for the duration of the program. – JustKash Mar 07 '20 at 19:27

1 Answers1

2

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());
    }
}
rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • Thank you, I needed to use ROOT_DIRECTORY.as_deref() to use a String literal. – Paul Mar 07 '20 at 19:27
  • @Paul: Indeed, fixed. – rodrigo Mar 07 '20 at 19:46
  • I'd like to recommend showing the solution with lazy loading using *lazy_static!* instead of *unsafe* code https://stackoverflow.com/a/51182014/4745695 – Polostor May 29 '20 at 13:33
  • 1
    @Polostor: The issue with `lazy_static!` is that the OP want to initialize the value with arguments from main, not with static data. But maybe a `once_cell`would be a good improvement. – rodrigo May 29 '20 at 15:03
  • Oh, I must've overlooked that CLI thingy. Well the `once_cell` is great fit for this. – Polostor May 30 '20 at 11:45