【问题标题】:Rust 'borrowed value does not live long enough' while assigning to a static variable分配给静态变量时,Rust“借来的值不够长”
【发布时间】:2020-03-07 18:13:42
【问题描述】:

我的目标是保留一个静态变量并让 CLI 参数覆盖该值,但我很难找到一种方法来保留从 args 迭代器获得的值的静态副本。

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.")
        }
      }
    }
  }
}

给出以下编译错误

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();
   | 

我有什么方法可以从迭代器中创建值的静态副本吗?

【问题讨论】:

  • args 列表中传递的内容不能存在于静态内存中;静态内存用于在程序执行期间存在的数据,用于作为程序二进制文件的一部分的数据。你能告诉我们你想要做什么而不是你想要编码吗?
  • 这毫无意义。静态内存适用于进程的生命周期,并且肯定会在应用程序执行期间发生变化。为什么不能提升一块内存不空闲?
  • 对不起,我的意思不是静态内存不能改变,它可以在 Rust 中不安全地改变。但是 arg 或 args 没有静态生命周期,因此不能保证在程序期间存在。

标签: rust static borrow-checker borrowing


【解决方案1】:

你不能那样做。静态变量必须是'static,即不能包含非'static 的生命周期。这就是为什么您可以在静态引用的声明中省略生命周期。你的实际上相当于:

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

而你的args 是一个局部变量,所以对它的引用不是'static

我有什么方法可以从迭代器中创建值的静态副本吗?

最简单的选择是让静态变量拥有它的数据,而不是作为一个引用,也就是说,让它成为一个String。不幸的是,静态构造函数必须是const,而我所知道的String 的唯一const 构造函数是String::new()。您可以添加一个辅助函数fn get_root_directory() -&gt; &amp;'static str,它读取全局变量并在未设置时返回默认值,但如果您对此感兴趣,您可以将静态设置为Option&lt;String&gt;

static mut ROOT_DIRECTORY: Option<String> = None;

pub fn get_root_directory() -> &'static str {
    unsafe {
        ROOT_DIRECTORY.as_deref().unwrap_or("C:\\test\\source")
    }
}

另一种选择是泄漏堆分配的字符串以使其成为静态。只要您只分配一次,泄漏应该不是问题。比如:

static mut ROOT_DIRECTORY: &'static str = "default value";

fn main() {
    let x = "...".to_string();
    unsafe {
        ROOT_DIRECTORY = Box::leak(x.into_boxed_str());
    }
}

【讨论】:

  • 谢谢,我需要使用 ROOT_DIRECTORY.as_deref() 来使用字符串文字。
  • @Paul:确实,已修复。
  • 我想推荐使用 lazy_static! 而不是 unsafe 代码stackoverflow.com/a/51182014/4745695 来展示延迟加载的解决方案
  • @Polostor:lazy_static! 的问题是 OP 希望使用来自 main 的参数初始化值,而不是使用静态数据。但也许once_cell会是一个很好的改进。
  • 哦,我一定忽略了那个 CLI 东西。那么once_cell 非常适合这个。
猜你喜欢
  • 2020-07-23
  • 1970-01-01
  • 2014-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-29
相关资源
最近更新 更多