Rust中的惰性只读“属性”

jm81lzqq  于 2023-01-26  发布在  其他
关注(0)|答案(2)|浏览(181)

在Rust中给出以下结构:

struct OrderLine {
    price: f32,
    quantity: f32,
}

impl OrderLine {
    fn total(&self) -> f32 {
        println!("total has been computed"); // this is used in the test bellow
        self.price * self.quantity
    }
}

我如何:
1.对于此结构体的每个示例,只计算total值一次,即使此函数被多次调用(请参见下面的测试,了解预期行为的示例)。total值必须是惰性计算的。我不希望在初始化结构体时预先计算它,例如在OrderLine::new函数中。
1.保持total和下划线值(pricequantity)之间的一致性:
1.如果我们允许它们改变,那么total必须在下次被调用时重新计算。
1.或者,如果这不可能或太困难,则使此结构不可变以防止更改。

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

    #[test]
    fn test_total_must_be_computed_only_once() {
        let order_line = OrderLine {
            price: 10.0,
            quantity: 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());

        // The actual output is:
        // before calling total for the first time
        // total has been computed
        // 20
        // before calling total for the second time
        // total has been computed                  <- repeated
        // 20

        // The expected output is:
        // before calling total for the first time
        // total has been computed                  <- no repetition
        // 20
        // before calling total for the second time
        // 20
    }
}
up9lanfz

up9lanfz1#

另一种方法是使用OnceCell,其优点(与Option方法相比)是不需要&mut self访问:

// in the process of being added to the standard library, but not there yet
use once_cell::unsync::OnceCell;

pub struct OrderLine {
    price: f32,
    quantity: f32,
    total: OnceCell<f32>,
}

impl OrderLine {
    pub fn new(price: f32, quantity: f32) -> Self {
        OrderLine {
            price,
            quantity,
            total: OnceCell::new(),
        }
    }

    pub fn total(&self) -> f32 {
        // calculate the total if not already calculated
        *self.total.get_or_init(|| {
            println!("COMPUTED"); // this is used in the test bellow
            self.price * self.quantity
        })
    }

    pub fn set_price(&mut self, price: f32) {
        self.price = price;
        // clear the previous calculated total
        self.total = OnceCell::new();
    }

    pub fn set_quantity(&mut self, quantity: f32) {
        self.quantity = quantity;
        // clear the previous calculated total
        self.total = OnceCell::new();
    }
}

playground

bis0qfac

bis0qfac2#

下面是使用Option的一种方法:

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

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

    pub fn total(&mut self) -> f32 {
        *self.total.get_or_insert_with(|| {
            let total = self.price * self.quantity;
            println!("total has been computed"); // this is used in the test bellow
            total
        })
    }

    pub fn set_price(&mut self, price: f32) {
        self.price = price;
        self.total = None;
    }

    pub fn set_quantity(&mut self, quantity: f32) {
        self.quantity = quantity;
        self.total = None;
    }

    pub fn price(&self) -> f32 {
        self.price
    }

    pub fn quantity(&self) -> f32 {
        self.quantity
    }
}

#[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());

        order_line.set_price(500.0);
        println!("before calling total for the third time");
        println!("{}", order_line.total());
    }
}

相关问题