什么时候将引用的生命周期延长到 Arenas 是安全的?

When is it safe to extend the lifetime of references into Arenas?

我有一个使用 Arena:

的结构
struct Foo<'f> {
    a: Arena<u64>, // from the typed-arena crate
    v: Vec<&'f u64>,
}

将引用的生命周期延长到 arena 中是否安全,只要它受主结构的生命周期约束?

impl<'f> Foo<'f> {
    pub fn bar(&mut self, n: u64) -> Option<&'f u64> {
        if n == 0 {
            None
        } else {
            let n_ref = unsafe { std::mem::transmute(self.a.alloc(n)) };
            Some(n_ref)
        }
    }
}

有关更多上下文,请参阅此 Reddit comment

Is it safe to extend the lifetime of a reference into the arena so long as it is bound by the lifetime of the main struct?

Arena 将与 Foo 一起删除,因此,原则上,这个 是安全的,但也没有必要,因为 Arena 已经活得够久了。

但是,这并不是您的代码实际执行的操作!生命周期 'f 可能比结构体的生命周期更长——它可以与 v 中的 shortest-lived 引用一样长。例如:

fn main() {
    let n = 1u64;
    let v = vec![&n];
    let bar;
    {
        let mut foo = Foo { a: Arena::new(), v };
        bar = foo.bar(2);
        // foo is dropped here, along with the Arena
    }
    // bar is still useable here because 'f is the full scope of `n`
    println!("bar = {:?}", bar); // Some(8021790808186446178) - oops!
}

试图假装生命周期比实际更长,这为安全代码中的未定义行为创造了机会。


一个可能的解决方法是在 struct 之外拥有 Arena 并依靠借用检查器来确保它在仍在使用时不会被丢弃:

struct Foo<'f> {
    a: &'f Arena<u64>,
    v: Vec<&'f u64>,
}

impl<'f> Foo<'f> {
    pub bar(&mut self, n: u64) -> Option<&'f u64> {
        if n == 0 {
            None
        } else {
            Some(self.a.alloc(n))
        }
    }
}

fn main() {
    let arena = Arena::new();
    let n = 1u64;
    let v = vec![&n];
    let bar;
    {
        let mut foo = Foo { a: &arena, v };
        bar = foo.bar(2);
    }
    println!("bar = {:?}", bar); // Some(2)
}

就像您的不安全版本一样,生命周期表示对 Arena 的引用必须至少与 Vec 中的项目一样有效。然而,这也强制了这个事实是真实的!由于没有不安全代码,您可以相信 borrow-checker 这不会触发 UB。