在自引用结构中使用 'static 来引用 Box 可以吗?

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

给出下面的代码片段:

use std::{io::BufWriter, pin::Pin};

pub struct SelfReferential {
    pub writer: BufWriter<&'static mut [u8]>, // borrowed from buffer
    pub buffer: Pin<Box<[u8]>>, 
}

#[cfg(test)]
mod tests {
    use std::io::Write;

    use super::*;
    fn init() -> SelfReferential {
        let mut buffer = Pin::new(vec![0; 12].into_boxed_slice());
        let writer = unsafe { buffer.as_mut().get_unchecked_mut() };
        let writer = unsafe { (writer as *mut [u8]).as_mut().unwrap() };
        let writer = BufWriter::new(writer);
        SelfReferential { writer, buffer }
    }

    #[test]
    fn move_works() {
        let mut sr = init();
        sr.writer.write(b"hello ").unwrap();
        sr.writer.flush().unwrap();
        let mut slice = &mut sr.buffer[6..];
        slice.write(b"world!").unwrap();
        assert_eq!(&sr.buffer[..], b"hello world!".as_ref());

        let mut sr_moved = sr;
        sr_moved.writer.write(b"W").unwrap();
        sr_moved.writer.flush().unwrap();
        assert_eq!(&sr_moved.buffer[..], b"hello World!".as_ref());
    }
}

第一个问题:将

'static
生命周期分配给
BufWriter
中的可变切片引用是否可以?从技术上讲,它与结构实例本身的生命周期有关,并且据我所知,没有安全的方法可以使其无效。

第二个问题:除了这种类型的不安全实例化这一事实之外,在测试示例中,在底层缓冲区中创建了两个可变引用,是否存在与这种“unidiomatic”(因为缺乏更好的词)类型相关的任何其他潜在危险?

rust undefined-behavior unsafe self-reference
1个回答
1
投票

可以将

'static
生命周期分配给
BufWriter
中的可变切片引用吗?

有点像,但还有一个更大的问题。生命周期本身并不比任何其他选择差,因为您可以在这里使用非常准确的“没有”生命周期。但暴露该引用并不安全,因为这样就可以采取: let w = BufWriter<&'static mut [u8]> = { let sr = init(); sr.writer }; // `sr.buffer` has now been dropped, so `w` has a dangling reference

这种“不惯用”(因为缺乏更好的词)类型是否存在任何其他潜在危险?

是的,这是未定义的行为。
Box

不只是

管理分配;它还(当前)表明对内容的唯一、非别名访问的声明。您可以通过创建 
writer 然后移动 buffer
 来违反非锯齿 — 即使实际上并未触及 
堆内存
buffer 的移动也会被计算在内,从而使所有对其的引用无效。
这是 Rust 语义的一个领域,尚未完全确定,但就当前编译器而言,这是 UB。如果您在 Miri 解释器下运行测试代码,您可以看到这一点。

好消息是,您正在尝试做的是一个非常普遍的愿望,并且人们已经在解决这个问题。我个人建议使用


ouroboros

— 在宏的帮助下,它允许您创建所需的结构

,而无需编写任何新的不安全代码。
如何使用 writer 会有一些限制,但是没有什么是你通过写下 impl io::Write for SelfReferential
 无法解决的。这个空间中另一个较新的库是
yoke
;我没试过。
    

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