Rust自引用结构中的引用

92dk7w1h  于 2023-02-08  发布在  其他
关注(0)|答案(1)|浏览(154)

给定以下代码片段:

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中的可变切片引用?从技术上讲,它与结构体示例本身的生存期绑定,而且AFAIK没有安全的方法使其无效。
第二个问题:除了这个类型的不安全示例化之外,在测试示例中,创建了两个对底层缓冲区的可变引用,是否存在与这种"unidiomatic"(因为缺少更好的词)类型相关的任何其他潜在危险?

ckocjqey

ckocjqey1#

是否可以将'static生存期分配给BufWriter中可变切片引用?
差不多吧,但是还有一个更大的问题,lifetime本身并不比其他选择差,因为这里没有lifetime可以使用,这是非常准确的,但是公开引用是不安全的,因为这样就可以得到:

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;我没试过。

相关问题