Rust编译报错:main函数结束时引用仍被借用,求解决方案
Rust实现光线追踪时的生命周期借用问题解决
问题背景
我正在学习《Ray Tracing in One Weekend》并使用Rust实现。代码中定义了Hittable和Material两个trait,Hittable trait为Sphere等对象实现hit方法,每种对象包含对应材质(如Glass)。实际项目中,Sphere结构体和HitRecord结构体(用于hit方法传递结果的可变引用)均包含&dyn Material,因此为相关trait和结构体添加了生命周期参数。但编译时编译器提示main函数结束时引用仍处于借用状态。
问题代码
trait Hittable<'a> { fn hit(&self); } trait Material { fn result(&self); } struct Glass; impl Material for Glass { fn result(&self) { println!("Glass is broken!"); } } struct Sphere<'a> { name: String, mat_ptr: &'a dyn Material, } impl<'a> Hittable<'a> for Sphere<'a> { fn hit(&self) { println!("Name is {}", self.name); self.mat_ptr.result(); } } struct HT<'a> { pub objects: Vec<Box<dyn Hittable<'a>>>, } fn main() { let mut list = HT { objects: vec![] }; let surface_material = Glass; let s = Sphere { name: String::from("球"), mat_ptr: &surface_material, }; list.objects.push(Box::new(s)); }
错误信息
Compiling rust_test v0.1.0 (/home/hnyls2002/rust_test) error[E0597]: `surface_material` does not live long enough --> src/main.rs:38:18 | 38 | mat_ptr: &surface_material, | ^^^^^^^^^^^^^^^^^ | | | borrowed value does not live long enough | cast requires that `surface_material` is borrowed for `'static` ... 41 | } | - `surface_material` dropped here while still borrowed For more information about this error, try `rustc --explain E0597`.
问题原因
错误核心在于:将Sphere包装进Box<dyn Hittable<'a>>时,Rust默认要求该trait对象满足'static生命周期(除非显式标注)。而surface_material是main函数栈上的局部变量,生命周期仅到main函数结束,但list持有了对它的引用,导致引用生命周期短于被引用值的销毁时机,违反了Rust的借用规则。
解决方案
方案1:使用引用计数智能指针(推荐)
光线追踪场景中多个物体可能共享同一种材质,用Arc<dyn Material>可安全共享材质实例,彻底规避生命周期问题:
use std::sync::Arc; trait Hittable { fn hit(&self); } trait Material { fn result(&self); } struct Glass; impl Material for Glass { fn result(&self) { println!("Glass is broken!"); } } struct Sphere { name: String, mat_ptr: Arc<dyn Material>, } impl Hittable for Sphere { fn hit(&self) { println!("Name is {}", self.name); self.mat_ptr.result(); } } struct HT { pub objects: Vec<Box<dyn Hittable>>, } fn main() { let mut list = HT { objects: vec![] }; let surface_material = Arc::new(Glass); let s = Sphere { name: String::from("球"), mat_ptr: surface_material.clone(), }; list.objects.push(Box::new(s)); }
方案2:调整变量声明顺序
利用Rust反向销毁变量的规则,让被引用的材质先声明、后销毁,保证其生命周期长于持有引用的list:
trait Hittable<'a> { fn hit(&self); } trait Material { fn result(&self); } struct Glass; impl Material for Glass { fn result(&self) { println!("Glass is broken!"); } } struct Sphere<'a> { name: String, mat_ptr: &'a dyn Material, } impl<'a> Hittable<'a> for Sphere<'a> { fn hit(&self) { println!("Name is {}", self.name); self.mat_ptr.result(); } } struct HT<'a> { pub objects: Vec<Box<dyn Hittable<'a>>>, } fn main() { let surface_material = Glass; let mut list = HT { objects: vec![] }; let s = Sphere { name: String::from("球"), mat_ptr: &surface_material, }; list.objects.push(Box::new(s)); }
方案3:将材质移到堆上获得'static生命周期
把材质实例分配到堆上,使其拥有'static生命周期,满足trait对象的约束:
trait Hittable<'a> { fn hit(&self); } trait Material { fn result(&self); } struct Glass; impl Material for Glass { fn result(&self) { println!("Glass is broken!"); } } struct Sphere<'a> { name: String, mat_ptr: &'a dyn Material, } impl<'a> Hittable<'a> for Sphere<'a> { fn hit(&self) { println!("Name is {}", self.name); self.mat_ptr.result(); } } struct HT<'a> { pub objects: Vec<Box<dyn Hittable<'a>>>, } fn main() { let mut list = HT { objects: vec![] }; // 将材质分配到堆上,获得'static生命周期 let surface_material = Box::new(Glass) as Box<dyn Material>; let s = Sphere { name: String::from("球"), mat_ptr: &*surface_material, }; list.objects.push(Box::new(s)); // 注意:surface_material需与list同生命周期,不能提前销毁 }
内容的提问来源于stack exchange,提问作者hnyls2002
相关产品推荐
相关产品推荐

