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

Rust中实现延迟计算且仅计算一次的只读属性方案

实现延迟计算且缓存的OrderLine total方法

需求回顾

  • total方法需延迟计算:仅在首次调用时计算结果,后续调用直接复用缓存值,禁止在结构体初始化时预计算
  • 保持结果一致性:
    • 若结构体字段允许修改,修改后下次调用total必须重新计算
    • 若可变逻辑实现难度大,可将结构体设为不可变以避免修改

方案1:不可变结构体(简单实现)

将结构体设为不可变,通过Option<f32>缓存计算结果,首次调用total时执行计算并填充缓存,后续直接返回缓存值。

实现代码

struct OrderLine {
    price: f32,
    quantity: f32,
    cached_total: Option<f32>,
}

impl OrderLine {
    // 构造函数初始化时缓存为空,保证延迟计算
    fn new(price: f32, quantity: f32) -> Self {
        Self {
            price,
            quantity,
            cached_total: None,
        }
    }

    fn total(&mut self) -> f32 {
        // 优先读取缓存,未计算则执行计算并缓存
        if let Some(total) = self.cached_total {
            total
        } else {
            println!("total has been computed");
            let total = self.price * self.quantity;
            self.cached_total = Some(total);
            total
        }
    }
}

测试验证

修改题目测试代码后运行:

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_total_must_be_computed_only_once() {
        let mut order_line = OrderLine::new(10.0, 2.0);
        println!("before calling total for the first time");
        println!("{}", order_line.total());
        println!("before calling total for the second time");
        println!("{}", order_line.total());

        // 预期输出:
        // before calling total for the first time
        // total has been computed
        // 20
        // before calling total for the second time
        // 20
    }
}

运行后total has been computed仅打印一次,完全符合预期。

补充:内部可变性版本(total接收&self)

如果需要total方法在不可变引用下调用,使用RefCell实现内部可变性:

use std::cell::RefCell;

struct OrderLine {
    price: f32,
    quantity: f32,
    cached_total: RefCell<Option<f32>>,
}

impl OrderLine {
    fn new(price: f32, quantity: f32) -> Self {
        Self {
            price,
            quantity,
            cached_total: RefCell::new(None),
        }
    }

    fn total(&self) -> f32 {
        let mut cached = self.cached_total.borrow_mut();
        if let Some(total) = *cached {
            total
        } else {
            println!("total has been computed");
            let total = self.price * self.quantity;
            *cached = Some(total);
            total
        }
    }
}

测试时无需将order_line声明为mut,同样能实现缓存效果。


方案2:支持字段修改的可变结构体

允许修改price和quantity,每次修改字段时清空缓存,确保下次调用total时重新计算。

实现代码

use std::cell::RefCell;

struct OrderLine {
    price: RefCell<f32>,
    quantity: RefCell<f32>,
    cached_total: RefCell<Option<f32>>,
}

impl OrderLine {
    fn new(price: f32, quantity: f32) -> Self {
        Self {
            price: RefCell::new(price),
            quantity: RefCell::new(quantity),
            cached_total: RefCell::new(None),
        }
    }

    // 修改price并清空缓存
    fn set_price(&self, new_price: f32) {
        *self.price.borrow_mut() = new_price;
        *self.cached_total.borrow_mut() = None;
    }

    // 修改quantity并清空缓存
    fn set_quantity(&self, new_quantity: f32) {
        *self.quantity.borrow_mut() = new_quantity;
        *self.cached_total.borrow_mut() = None;
    }

    fn total(&self) -> f32 {
        let mut cached = self.cached_total.borrow_mut();
        if let Some(total) = *cached {
            total
        } else {
            println!("total has been computed");
            let total = *self.price.borrow() * *self.quantity.borrow();
            *cached = Some(total);
            total
        }
    }
}

测试验证

新增测试验证修改字段后的重新计算逻辑:

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_total_recalculates_after_modification() {
        let order_line = OrderLine::new(10.0, 2.0);
        println!("First total call: {}", order_line.total()); // 打印计算日志,结果20
        println!("Second total call: {}", order_line.total()); // 无日志,结果20

        // 修改price
        order_line.set_price(15.0);
        println!("Third total call: {}", order_line.total()); // 再次打印计算日志,结果30
        println!("Fourth total call: {}", order_line.total()); // 无日志,结果30
    }
}

测试输出中,第一次和第三次调用total时会打印total has been computed,符合修改后重新计算的需求。


内容的提问来源于stack exchange,提问作者Rafael Amadigi Dal Santo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 08:38:49