Rust闭包借用问题:如何让闭包调用后归还借用?
Rust借用检查器与闭包相关问题解析
问题背景
原函数代码
fn update_acc(&mut self, acc_rub: &Vector3<f32>, _t: u64) -> () { let acc = Self::rub_to_frd(acc_rub); if acc.norm() < 1.0 { return (); // almost in free fall, or acc disabled, do not correct } let delta_opt = || -> Option<UnitQuaternion<f32>> { let uncorrected = self.attitude.inverse().transform_vector(&Self::UP_FRD); let delta_opt = UnitQuaternion::rotation_between(&acc, &uncorrected); delta_opt }; let dd = delta_opt(); match dd { Some(delta) => { self.inconsistency = self.inconsistency * Self::INCONSISTENCY_DECAY + delta.angle(); match UnitQuaternion::try_slerp( &UnitQuaternion::identity(), &delta, Self::BASE_GRAV_RATIO, 0.01, ) { Some(correction) => { let new_attitude = self.attitude * correction; self.attitude = new_attitude; { // TODO: verification code, clean up let residual = delta_opt().unwrap().angle().abs(); if (residual >= 0.01) { println!("!!!!!!!!!! residual: {} !!!!!!!!!!", residual); } } } None => { // TODO: opposite direction, don't know how to correct } }; } None => { // no error no update } } }
编译错误信息
error[E0506]: cannot assign to `self.attitude` because it is borrowed --> src/naive_cf.rs:160:25 | 140 | let delta_opt = || -> Option<UnitQuaternion<f32>> { | --------------------------------- `self.attitude` is borrowed here 141 | let uncorrected = self.attitude.inverse().transform_vector(&Self::UP_FRD); | ------------- borrow occurs due to use in closure ... 160 | self.attitude = new_attitude; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.attitude` is assigned to here but it was already borrowed ... 164 | let residual = delta_opt().unwrap().angle().abs(); | --------- borrow later used here
无错误的对比函数
fn dummy(&mut self) -> () { let delta_opt = || -> (Option<f32>) { let a = self.attitude.inverse(); Some(a.i) }; match delta_opt() { Some(v) => { self.attitude = UnitQuaternion::identity(); } None => {} } }
技术问题
- 为何
dummy函数中的闭包不会干扰self.attitude的借用状态?两者差异是什么?
- 为何
- 如何修改
delta_opt闭包,使其在调用结束后归还self.attitude的借用?
- 如何修改
- 如何重写
delta_opt闭包,使其能被编译器内联展开调用?
- 如何重写
问题解答
问题1解答
核心差异在于闭包的使用次数和生命周期:
dummy函数中的闭包仅被调用一次,调用完成后闭包不再被使用,编译器能明确推断出对self.attitude的不可变借用已经完全释放,因此后续的可变赋值操作合法。- 原函数中的
delta_opt闭包被调用两次:第一次调用后闭包仍被保留,用于后续的验证逻辑,编译器会认为self.attitude的不可变借用从闭包创建一直持续到第二次调用结束,中间对self.attitude的可变赋值需要独占借用,两者冲突导致报错。
问题2解答
要让闭包调用后立即归还借用,核心是避免闭包直接捕获self,改为捕获独立的变量副本或使用传参的方式:
方案1:捕获变量副本
提前将self.attitude和acc的副本取出,让闭包捕获这些副本而非self:
fn update_acc(&mut self, acc_rub: &Vector3<f32>, _t: u64) -> () { let acc = Self::rub_to_frd(acc_rub); if acc.norm() < 1.0 { return (); } // 提前取出当前attitude的副本(如果UnitQuaternion实现Copy可直接赋值,无需clone) let current_attitude = self.attitude.clone(); let acc_copy = acc.clone(); let delta_opt = || -> Option<UnitQuaternion<f32>> { let uncorrected = current_attitude.inverse().transform_vector(&Self::UP_FRD); UnitQuaternion::rotation_between(&acc_copy, &uncorrected) }; let dd = delta_opt(); // 后续逻辑不变... }
方案2:改用普通函数传参
完全放弃闭包,把逻辑抽成接收参数的函数,调用时传入self.attitude的引用,调用结束后借用自动释放:
impl YourStruct { fn compute_delta(attitude: &UnitQuaternion<f32>, acc: &Vector3<f32>) -> Option<UnitQuaternion<f32>> { let uncorrected = attitude.inverse().transform_vector(&Self::UP_FRD); UnitQuaternion::rotation_between(acc, &uncorrected) } fn update_acc(&mut self, acc_rub: &Vector3<f32>, _t: u64) -> () { let acc = Self::rub_to_frd(acc_rub); if acc.norm() < 1.0 { return (); } let dd = Self::compute_delta(&self.attitude, &acc); // 后续逻辑中调用时直接传参:Self::compute_delta(&self.attitude, &acc) } }
问题3解答
要让编译器内联闭包逻辑,最可靠的方式是改用普通函数并配合优化提示:
方案1:改用带内联属性的成员函数
将闭包逻辑转为成员函数,添加#[inline]属性提示编译器内联,同时开启编译优化(--release模式):
impl YourStruct { // 强制编译器尝试内联该函数 #[inline(always)] fn compute_delta(&self, acc: &Vector3<f32>) -> Option<UnitQuaternion<f32>> { let uncorrected = self.attitude.inverse().transform_vector(&Self::UP_FRD); UnitQuaternion::rotation_between(acc, &uncorrected) } fn update_acc(&mut self, acc_rub: &Vector3<f32>, _t: u64) -> () { let acc = Self::rub_to_frd(acc_rub); if acc.norm() < 1.0 { return (); } let dd = self.compute_delta(&acc); match dd { Some(delta) => { self.inconsistency = self.inconsistency * Self::INCONSISTENCY_DECAY + delta.angle(); match UnitQuaternion::try_slerp( &UnitQuaternion::identity(), &delta, Self::BASE_GRAV_RATIO, 0.01, ) { Some(correction) => { let new_attitude = self.attitude * correction; self.attitude = new_attitude; { let residual = self.compute_delta(&acc).unwrap().angle().abs(); if residual >= 0.01 { println!("!!!!!!!!!! residual: {} !!!!!!!!!!", residual); } } } None => {} }; } None => {} } } }
方案2:手动内联(冗余但直接)
直接将闭包内的代码复制到两次调用的位置,完全消除闭包,确保逻辑被内联:
fn update_acc(&mut self, acc_rub: &Vector3<f32>, _t: u64) -> () { let acc = Self::rub_to_frd(acc_rub); if acc.norm() < 1.0 { return (); } // 第一次调用逻辑直接展开 let uncorrected_first = self.attitude.inverse().transform_vector(&Self::UP_FRD); let dd = UnitQuaternion::rotation_between(&acc, &uncorrected_first); match dd { Some(delta) => { self.inconsistency = self.inconsistency * Self::INCONSISTENCY_DECAY + delta.angle(); match UnitQuaternion::try_slerp( &UnitQuaternion::identity(), &delta, Self::BASE_GRAV_RATIO, 0.01, ) { Some(correction) => { let new_attitude = self.attitude * correction; self.attitude = new_attitude; { // 第二次调用逻辑直接展开 let uncorrected_residual = self.attitude.inverse().transform_vector(&Self::UP_FRD); let residual = UnitQuaternion::rotation_between(&acc, &uncorrected_residual) .unwrap() .angle() .abs(); if residual >= 0.01 { println!("!!!!!!!!!! residual: {} !!!!!!!!!!", residual); } } } None => {} }; } None => {} } }
内容的提问来源于stack exchange,提问作者tribbloid
相关产品推荐
相关产品推荐

