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

问题描述 投票:10回答:1

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

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中编译它吗?

rust borrow-checker
1个回答
13
投票

甚至可以在安全的Rust中编译它吗?

不。在一般情况下,您要做的事情本质上是不安全的。

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

注意:范围中的值将按照创建它们的相反顺序删除

正如编译器告诉您的那样,您需要重新排序代码。您实际上没有说出“与代码组织相关的原因”的限制是什么,但直接修复是:

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]功能将此信息传达给编译器。

也可以看看:

© www.soinside.com 2019 - 2024. All rights reserved.