[英]Closures exercise in Chapter 13 of the Rust book
我正在阅读 Rust 书,目前我对第 13 章关于闭包的练习的解决方案有点困惑( https://doc.rust-lang.org/book/ch13-01-closures.ZAFC369E833 ) .
如果我的问题没有多大意义,我想提前道歉。 我还有很多东西要学。 如果我的问题过于宽泛,请随时向我指出具体的文档。
代码似乎运行良好,但我不太了解以下内容:
self.map.get()
需要参考? map
字段的类型为HashMap
,这不作为参考,而是 u32。match
下使用Some(v)
和None
?*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.