为什么 HashMap 有 iter_mut() 而 HashSet 没有?
Why does HashMap have iter_mut() but HashSet doesn't?
在 Rust 中为 HashMap
but not HashSet
提供 iter_mut
函数的设计原理是什么?
自己动手(假设甚至可以做到)会不会失礼?
拥有一个可以缓解引起
的情况
previous borrow of X
occurs here; the immutable borrow prevents
subsequent moves or mutable borrows of X
until the borrow ends
例子
An extremely convoluted example (Gist) 没有说明为什么参数传递是这样的。有一条解释痛点的简短评论:
use std::collections::HashSet;
fn derp(v: i32, unprocessed: &mut HashSet<i32>) {
if unprocessed.contains(&v) {
// Pretend that v has been processed
unprocessed.remove(&v);
}
}
fn herp(v: i32) {
let mut unprocessed: HashSet<i32> = HashSet::new();
unprocessed.insert(v);
// I need to iterate over the unprocessed values
while let Some(u) = unprocessed.iter().next() {
// And them pass them mutably to another function
// as I will process the values inside derp and
// remove them from the set.
//
// This is an extremely convoluted example but
// I need for derp to be a separate function
// as I will employ recursion there, as it is
// much more succinct than an iterative version.
derp(*u, &mut unprocessed);
}
}
fn main() {
println!("Hello, world!");
herp(10);
}
声明
while let Some(u) = unprocessed.iter().next() {
是不可变的借用,因此
derp(*u, &mut unprocessed);
不可能,因为未处理的不能可变地借用。不可变借用直到 while 循环结束才结束。
我曾尝试使用 this as reference 并最终试图通过各种排列的赋值来欺骗借用检查器,包括大括号,但由于预期表达式的耦合,问题仍然存在。
你必须考虑什么 HashSet
actually is. The IterMut
that you get from HashMap::iter_mut()
只在值部分可变:(&key, &mut val)
, ((&'a K, &'a mut V)
)
HashSet
基本上是一个 HashMap<T, ()>
,因此实际值是键,如果您要修改键,则必须更新它们的哈希值,否则您会得到无效的 HashMap
.
如果您的 HashSet
包含 Copy
类型,例如 i32
,您可以处理该值的副本以尽早释放 HashSet
上的借用.为此,您需要消除 while let
表达式中绑定的所有借用。在您的原始代码中,u
的类型为 &i32
,并且它一直从 unprocessed
借用,直到循环结束。如果我们将模式更改为 Some(&u)
,则 u
的类型为 i32
,它不会借用任何东西,因此我们可以随意使用 unprocessed
.
fn herp(v: i32) {
let mut unprocessed: HashSet<i32> = HashSet::new();
unprocessed.insert(v);
while let Some(&u) = unprocessed.iter().next() {
derp(u, &mut unprocessed);
}
}
如果类型不是 Copy
或对于 copy/clone 来说太贵了,您可以将它们包装在 Rc
or Arc
, and clone them as you iterate on them using cloned()
中(克隆 Rc
或 Arc
不克隆基础值,它只是克隆 Rc
指针并增加引用计数器)。
use std::collections::HashSet;
use std::rc::Rc;
fn derp(v: &i32, unprocessed: &mut HashSet<Rc<i32>>) {
if unprocessed.contains(v) {
unprocessed.remove(v);
}
}
fn herp(v: Rc<i32>) {
let mut unprocessed: HashSet<Rc<i32>> = HashSet::new();
unprocessed.insert(v);
while let Some(u) = unprocessed.iter().cloned().next() {
// If you don't use u afterwards,
// you could also pass if by value to derp.
derp(&u, &mut unprocessed);
}
}
fn main() {
println!("Hello, world!");
herp(Rc::new(10));
}
在 Rust 中为 HashMap
but not HashSet
提供 iter_mut
函数的设计原理是什么?
自己动手(假设甚至可以做到)会不会失礼?
拥有一个可以缓解引起
的情况previous borrow of
X
occurs here; the immutable borrow prevents subsequent moves or mutable borrows ofX
until the borrow ends
例子
An extremely convoluted example (Gist) 没有说明为什么参数传递是这样的。有一条解释痛点的简短评论:
use std::collections::HashSet;
fn derp(v: i32, unprocessed: &mut HashSet<i32>) {
if unprocessed.contains(&v) {
// Pretend that v has been processed
unprocessed.remove(&v);
}
}
fn herp(v: i32) {
let mut unprocessed: HashSet<i32> = HashSet::new();
unprocessed.insert(v);
// I need to iterate over the unprocessed values
while let Some(u) = unprocessed.iter().next() {
// And them pass them mutably to another function
// as I will process the values inside derp and
// remove them from the set.
//
// This is an extremely convoluted example but
// I need for derp to be a separate function
// as I will employ recursion there, as it is
// much more succinct than an iterative version.
derp(*u, &mut unprocessed);
}
}
fn main() {
println!("Hello, world!");
herp(10);
}
声明
while let Some(u) = unprocessed.iter().next() {
是不可变的借用,因此
derp(*u, &mut unprocessed);
不可能,因为未处理的不能可变地借用。不可变借用直到 while 循环结束才结束。
我曾尝试使用 this as reference 并最终试图通过各种排列的赋值来欺骗借用检查器,包括大括号,但由于预期表达式的耦合,问题仍然存在。
你必须考虑什么 HashSet
actually is. The IterMut
that you get from HashMap::iter_mut()
只在值部分可变:(&key, &mut val)
, ((&'a K, &'a mut V)
)
HashSet
基本上是一个 HashMap<T, ()>
,因此实际值是键,如果您要修改键,则必须更新它们的哈希值,否则您会得到无效的 HashMap
.
如果您的 HashSet
包含 Copy
类型,例如 i32
,您可以处理该值的副本以尽早释放 HashSet
上的借用.为此,您需要消除 while let
表达式中绑定的所有借用。在您的原始代码中,u
的类型为 &i32
,并且它一直从 unprocessed
借用,直到循环结束。如果我们将模式更改为 Some(&u)
,则 u
的类型为 i32
,它不会借用任何东西,因此我们可以随意使用 unprocessed
.
fn herp(v: i32) {
let mut unprocessed: HashSet<i32> = HashSet::new();
unprocessed.insert(v);
while let Some(&u) = unprocessed.iter().next() {
derp(u, &mut unprocessed);
}
}
如果类型不是 Copy
或对于 copy/clone 来说太贵了,您可以将它们包装在 Rc
or Arc
, and clone them as you iterate on them using cloned()
中(克隆 Rc
或 Arc
不克隆基础值,它只是克隆 Rc
指针并增加引用计数器)。
use std::collections::HashSet;
use std::rc::Rc;
fn derp(v: &i32, unprocessed: &mut HashSet<Rc<i32>>) {
if unprocessed.contains(v) {
unprocessed.remove(v);
}
}
fn herp(v: Rc<i32>) {
let mut unprocessed: HashSet<Rc<i32>> = HashSet::new();
unprocessed.insert(v);
while let Some(u) = unprocessed.iter().cloned().next() {
// If you don't use u afterwards,
// you could also pass if by value to derp.
derp(&u, &mut unprocessed);
}
}
fn main() {
println!("Hello, world!");
herp(Rc::new(10));
}