繁体   English   中英

Rust 书第 13 章中的闭包练习

[英]Closures exercise in Chapter 13 of the Rust book

我正在阅读 Rust 书,目前我对第 13 章关于闭包的练习的解决方案有点困惑( https://doc.rust-lang.org/book/ch13-01-closures.ZAFC369E833 ) .

如果我的问题没有多大意义,我想提前道歉。 我还有很多东西要学。 如果我的问题过于宽泛,请随时向我指出具体的文档。

代码似乎运行良好,但我不太了解以下内容:

  1. 为什么self.map.get()需要参考? map字段的类型为HashMap ,这不作为参考,而是 u32。
  2. 我是否总是需要在match下使用Some(v)None
  3. 为什么我需要取消引用*v
use std::thread;
use std::time::Duration;
use std::collections::HashMap;

fn generate_workout(intensity: u32, random_number: u32) {
    let mut expensive_result = Cacher::new(|num| {
        println!("Calculating slowly...");
        thread::sleep(Duration::from_secs(2));
        num
    });

    if intensity < 25 {
        println!("Today, do {} pushups!", expensive_result.value(intensity));
        println!("Next, do {} situps!", expensive_result.value(intensity));
    } else {
        if random_number == 3 {
            println!("Take a break today! Remember to stay hydrated!");
        } else {
            println!("Today, run for {} minutes", expensive_result.value(intensity));
        }
    }
}

struct Cacher<T>
where
    T: Fn(u32) -> u32,
{
    calculation: T,
    // value: Option<u32>,
    map: HashMap<u32, u32>,
}

impl<T> Cacher<T>
where
    T: Fn(u32) -> u32,
{
    fn new(calculation: T) -> Cacher<T> {
        let map: HashMap<u32, u32> = HashMap::new();

        Cacher {
            calculation,
            map: map,
        }
    }

    fn value(&mut self, arg: u32) -> u32 {
        match self.map.get(&arg) {
            Some(v) => *v,
            None => {
                let v = (self.calculation)(arg);
                self.map.insert(arg, v);
                v
            }
        }
    }
}

fn main() {
    let simulated_user_specified_value = 10;
    let simulated_random_number = 7;

    generate_workout(simulated_user_specified_value, simulated_random_number);
}

#[test]
fn call_with_different_values() {
    let mut c = Cacher::new(|a| a);

    let v1 = c.value(1);
    let v2 = c.value(2);

    assert_eq!(v2, 2);
}

为什么 self.map.get() 需要参考?

因为 map 不需要获取作为参数传递的密钥的所有权。 get方法只需要 hash 的 key 即可最终定位到 entry,并将其与存储的 key 进行比较,以确保它不是 hash 冲突。 下面是get方法的声明:

pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> where
    K: Borrow<Q>,
    Q: Hash + Eq

我是否总是需要在匹配时使用 Some(v) 和 None ?

这个问题有点不清楚。 但是,如果我们关注这个特定的情况,由于HashMap::get()可以使用 map 中不包含的密钥调用,因此调用者需要一种方法来识别未找到密钥的情况。 这就是Option类型存在的原因。

Option是具有两个变体的枚举类型, Some(T) (存在值)和None (不存在值)。

由于match表达式支持使用枚举变体作为模式,因此以这种方式完成Option处理是非常常见和惯用的。

总是需要这样使用它吗? 不,还有其他选择:

通用模式:

match self.map.get(&arg) {
    Some(v) => *v,
    _ => {
        let v = (self.calculation)(arg);
        self.map.insert(arg, v);
        v
    }
}

由于 Option 只有两个变体, None模式可以替换为包罗万象的模式_ ,这意味着“匹配除 Some(v) 之外的所有内容”。 _通常用作匹配表达式,需要详尽无遗。

如果让:

if let Some(v) = self.map.get(&arg) {
    *v
} else {
    let v = (self.calculation)(arg);
    self.map.insert(arg, v);
    v
}

仅在处理两种情况时与match非常相似,但可以说更具可读性。

条目 API:

let e = self.map.entry(arg).or_insert_with(|| (self.calculation)(arg));
*e

这是设置 HashMap 条目(如果不存在)并获取值的一种非常方便的方法。 请注意,此处 map 拥有 arg 的所有权,因为它可能会将其插入 HashMap。

为什么我需要取消引用 *v?

这是因为get返回对 map 仍拥有的值的引用,所以它的类型是&u32

所有权 model 是 Rust 中 memory 管理最强大的方面之一。 在程序期间的任何时候,保证 memory 中的每个 object 都有一个所有者。

如果get返回的是值本身,而不是引用,则意味着该值的所有权将转移给调用者,并且 map 将无法再使用它。

另外,值得一提的是,像u32这样的整数是Copy types ,这意味着当它们像这样被取消引用时,它们实际上是被复制的,所以你从你的方法返回的值是仍然拥有的值的副本map。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM