[英]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.