在容器之后创建借用值时,如何添加对容器的引用?

How do I add references to a container when the borrowed values are created after the container?

由于与代码组织相关的原因,我需要编译器接受以下(简化的)代码:

fn f() {
    let mut vec = Vec::new();
    let a = 0;
    vec.push(&a);
    let b = 0;
    vec.push(&b);
    // Use `vec`
}

编译器报错

error: `a` does not live long enough
 --> src/main.rs:8:1
  |
4 |     vec.push(&a);
  |               - borrow occurs here
...
8 | }
  | ^ `a` dropped here while still borrowed
  |
  = note: values in a scope are dropped in the opposite order they are created

error: `b` does not live long enough
 --> src/main.rs:8:1
  |
6 |     vec.push(&b);
  |               - borrow occurs here
7 |     // Use `vec`
8 | }
  | ^ `b` dropped here while still borrowed
  |
  = note: values in a scope are dropped in the opposite order they are created

但是,我很难说服编译器在它引用的变量之前删除向量。 vec.clear() 不起作用,drop(vec) 也不起作用。 mem::transmute() 也不起作用(强制 vec'static)。

我找到的唯一解决方案是将引用转换为 &'static _。还有别的办法吗?甚至可以在安全的 Rust 中编译它吗?

Is it even possible to compile this in safe Rust?

没有。在一般情况下,您尝试做的是本质上不安全

该集合包含对将在删除集合本身之前删除的变量的引用。这意味着集合的析构函数可以访问 不再有效 的引用。析构函数可以选择取消引用其中一个值,从而破坏 Rust 的内存安全保证。

note: values in a scope are dropped in the opposite order they are created

正如编译器告诉您的那样,您需要重新排序代码。 您实际上并没有说 "reasons related to code organization" 的限制是什么 ,但直接的解决方法是:

fn f() {
    let a = 0;
    let b = 0;
    let mut vec = Vec::new();
    vec.push(&a);
    vec.push(&b);
}

一个不太明显的是:

fn f() {
    let a;
    let b;

    let mut vec = Vec::new();
    a = 0;
    vec.push(&a);
    b = 0;
    vec.push(&b);
}

综上所述,一旦启用 non-lexical lifetimes,您的原始代码就可以工作了!借用检查器变得更细化了一个值需要多长时间。

但是等等;我刚刚说过如果集合中的值在集合之前被删除,集合可能会访问无效内存,现在编译器允许这种情况发生?给出了什么?

这是因为标准库给我们开了个鬼把戏。 VecHashSet 等集合保证它们不会在析构函数中访问它们的泛型参数。他们使用 unstable #[may_dangle] 功能将此信息传达给编译器。

另请参阅:

  • Moved variable still borrowing after calling `drop`?
  • "cannot move out of variable because it is borrowed" when rotating variables