给定以下代码片段:
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"(因为缺少更好的词)类型相关的任何其他潜在危险?
1条答案
按热度按时间ckocjqey1#
是否可以将
'static
生存期分配给BufWriter
中可变切片引用?差不多吧,但是还有一个更大的问题,lifetime本身并不比其他选择差,因为这里没有lifetime可以使用,这是非常准确的,但是公开引用是不安全的,因为这样就可以得到:
是否存在与这种“非惯用”(因为缺乏更好的词)类型相关联的任何其他潜在危险?
是的,这是一个未定义的行为。
Box
不仅仅是管理一个分配;它还(目前)发出了对内容的唯一、非别名访问的声明,您通过创建writer
然后移动buffer
来违反该非别名-即使实际上没有触及 * 堆内存 ,buffer
的移动也会被视为使所有对它的引用无效。这是Rust语义的一个尚未完全确定的领域,但就当前编译器而言,这是UB。如果在Miri解释器下运行测试代码,您可以看到这一点。
好消息是,你尝试做的是一个非常普遍的愿望,人们已经解决了这个问题。我个人推荐使用
ouroboros
-在宏的帮助下,它允许你创建你想要的结构 * 而不需要编写任何新的不安全代码。 在如何使用writer
方面会有一些限制,但是通过编写impl io::Write for SelfReferential
,没有什么是您不能清理的。这个领域中另一个更新的库是yoke
;我没试过。