You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 => {}
    }
}

技术问题

    1. 为何dummy函数中的闭包不会干扰self.attitude的借用状态?两者差异是什么?
    1. 如何修改delta_opt闭包,使其在调用结束后归还self.attitude的借用?
    1. 如何重写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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 18:07:33