具有生命周期的结构体的Vec



我试图使用RustyBuzz做一些文本显示。整形是由一个名为Face<'a>的结构体完成的,该结构体包含对字体文件中字节的引用。我想允许加载字体"在飞行中",例如,用户输入字体文件的路径,文件被加载,一个新的Face<'a>被创建并添加到字体容器中。我怎么能创建这样一个字体容器,给定的生命周期参数在Face<'a>?

Naïvely,可以这样做:

#[derive(Default)]
struct FontAtlas<'a> {
fonts : Vec<Face<'a>>
}
impl<'a> FontAtlas<'a> {
fn add_new_font(&mut self, bytes : &'a [u8]) {
self.fonts.push(Face::from_slice(bytes, 0).unwrap());
}
}

在main函数中:

fn main() {
let mut atlas = FontAtlas::default();
loop {
let font_path  = get_user_input();
let font_bytes = std::fs::read(font_path).unwrap();
atlas.add_new_font(&font_bytes);
}
}

这不起作用,因为atlasfont_bytes寿命长。可以像这样使font_bytes寿命更长:

fn main() {
let mut font_data : Vec<Vec<u8>> = Vec::new(); // holds all bytes from font files
let mut atlas = FontAtlas::default();
loop {
let font_path  = get_user_input();
let font_bytes = std::fs::read(font_path).unwrap();
font_data.push(font_bytes); // mutable borrow invalidates all refs in atlas
atlas.add_new_font(font_data.last().unwrap()); // 'font_data.last()' will live longer than 'atlas', so we're good on this side
}
}

但是由于生命周期的限制,这违反了借用规则:必须不可变地为整个loop借用font_data,这阻止了向font_data推送所需的可变借用。

有什么方法可以实现字体加载"在飞行中"?或者这在本质上是"不安全的"?

这是一个hack,但是您可以泄漏Box以获得'static生命周期的引用:

fn main() {
let mut atlas = FontAtlas::default();
loop {
let font_path  = get_user_input();
let font_bytes = Box::leak(std::fs::read(font_path).unwrap().into_boxed_slice());
atlas.add_new_font(font_bytes);
}
}

这假定字节数据必须在程序的整个运行期间存在。

你可以总结这个基本的思想来创建一个"拥有的字节片注册表"的安全抽象。它也会清理自己的分配:

use std::cell::RefCell;
#[derive(Default)]
struct ByteRegistry(RefCell<Vec<*mut [u8]>>);
impl ByteRegistry {
pub fn new() -> Self {
Self::default()
}

// Note this can take self by shared reference due to the use of RefCell.
pub fn add(&self, bytes: impl Into<Box<[u8]>>) -> &[u8] {
let data = Box::into_raw(bytes.into());
self.0.borrow_mut().push(data);

// SAFETY: We own the data, and the reference is tied to our lifetime,
// so it will be released before we are dropped.
unsafe { &*data }
}
}
impl Drop for ByteRegistry {
fn drop(&mut self) {
for data in self.0.take().into_iter() {
// SAFETY: We obtained the pointers from Box::into_raw() and all
// borrows from add() have ended by now.
unsafe { drop(Box::from_raw(data)) }
}
}
}

最新更新