给出下面的代码片段:
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”(因为缺乏更好的词)类型相关的任何其他潜在危险?
可以将
生命周期分配给'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
;我没试过。