繁体   English   中英

具有生命周期的结构的 Vec

[英]Vec of struct with lifetimes

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

天真地,人们会做这样的事情:

#[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());
   }
}

然后示意性地,在主 function 中:

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);
   }
}

这不起作用,因为atlas font_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
   }
}

但是由于生命周期的限制,这违反了借用规则: font_data必须在整个loop中被不可变地借用,这阻止了推送到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)) }
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM