这是我的代码。
我正在尝试创建一个以 i32 作为键、以 HashSets 作为值的 Hashmap。但是当我编译时,我收到一条警告,指出变量(HashSet)不需要是可变的。但我显然正在向 HashSet 插入值。
use std::collections::*;
fn main() {
let mut map = HashMap::new();
for i in 0..3 {
// the warning will disappear if I remove the 'mut' keyword
let mut set = HashSet::new();
map.insert(i, set);
}
for (i, set) in &mut map {
for j in *i..(i+5) {
//here I am inserting values to the hashset , therefore set has to be mutable
set.insert(j);
}
}
for (i, set) in &map {
println!("{i} : {:?}", set);
}
}
Run Code Online (Sandbox Code Playgroud)
我收到的警告。
Compiling playground v0.0.1 (/playground)
warning: variable does not need to be mutable
--> src/main.rs:6:13
|
6 | let mut set = HashSet::new();
| ----^^^
| |
| help: remove this `mut`
|
= note: `#[warn(unused_mut)]` on by default
warning: `playground` (bin "playground") generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 2.84s
Running `target/debug/playground`
Run Code Online (Sandbox Code Playgroud)
如果我删除 mut 关键字,代码可以正常编译。你可以像我一样尝试在 Rust Playground 上运行它。
我尝试将元素插入到不可变的集合中,但失败了。
use std::collections::*;
fn main() {
let set = HashSet::new();
set.insert(1);
set.insert(2);
set.insert(3);
println!("{:?}", set);
}
Run Code Online (Sandbox Code Playgroud)
编译错误
Compiling playground v0.0.1 (/playground)
error[E0596]: cannot borrow `set` as mutable, as it is not declared as mutable
--> src/main.rs:5:5
|
4 | let set = HashSet::new();
| --- help: consider changing this to be mutable: `mut set`
5 | set.insert(1);
| ^^^^^^^^^^^^^ cannot borrow as mutable
error[E0596]: cannot borrow `set` as mutable, as it is not declared as mutable
--> src/main.rs:6:5
|
4 | let set = HashSet::new();
| --- help: consider changing this to be mutable: `mut set`
5 | set.insert(1);
6 | set.insert(2);
| ^^^^^^^^^^^^^ cannot borrow as mutable
error[E0596]: cannot borrow `set` as mutable, as it is not declared as mutable
--> src/main.rs:7:5
|
4 | let set = HashSet::new();
| --- help: consider changing this to be mutable: `mut set`
...
7 | set.insert(3);
| ^^^^^^^^^^^^^ cannot borrow as mutable
For more information about this error, try `rustc --explain E0596`.
error: could not compile `playground` due to 3 previous errors
Run Code Online (Sandbox Code Playgroud)
那么为什么编译器说变量集不需要声明为可变的呢?
如果你拥有对象,Rust 中的不变性与 C++ 中的不变性不同。拥有的东西总是可变的,因为,为什么不呢?你是主人。你可以变异。当我们谈论借用时,可变性与 C++ const 很接近。
因此,当您使用 创建集合时let set = HashSet::new();,变量名称set是不可变的。但创建的对象是可变的。并且插入函数获取了集合的所有权,因此它可以发生变化。
一个更简洁的例子可能是:
Run Code Online (Sandbox Code Playgroud)let set: HashSet<i32> = HashSet::new(); let mut set2 = set;
您始终可以将您拥有的不可变对象移动到可变对象中,这实际上只是重命名它并使其可变(这里没有复制或克隆)。
同时,作为参考,保持恒定的正确性:
Run Code Online (Sandbox Code Playgroud)let mut set: HashSet<i32> = HashSet::new(); let set_ref: &HashSet<i32> = &set; let set_mutref: &mut HashSet<i32> = set_ref; // compile error // ^^^^^^^ types differ in mutability
TLDR:当您调用时,map.insert(set)您将所有权(以及因此的可变性)从 传递set给map。
更长的答案:
in提供mut了let mut set = HashSet::new();以下额外功能:
set = other_hash_set;重新分配值&mut set以获得对该集合的可变引用当您调用set.insert(something)(when sethas type时HashSet,编译器会将其转换为HashSet::insert(&mut set, something),它显然使用上面的选项 2,因此mut需要关键字。
但你的代码永远不会这样做。在你的循环中,你写for (i, set) in &mut map. 在本例中,因为&mut HashMap<K, V>Implements IntoIterator,所以这是有效的,并且每个元素的类型是(&K, &mut V)。所以set这里有 type &mut HashSet<i32>,所以mut绑定上的关键字是不需要的。
我喜欢将mut这里视为“变量名称的一部分”。这解释了为什么您可以执行以下操作:
let mut i = 5;
let j = i;
let mut k = j;
Run Code Online (Sandbox Code Playgroud)
还值得记住的是,Rust 中的可变性通常不会像其他语言中那样存在相同的陷阱。
如果您正在使用引用,则会按照您的预期强制执行可变性。但是,如果您正在使用自有数据,则不可能存在一些在其下方发生数据更改的剩余引用。一旦您对拥有的数据进行操作,您就知道没有其他人可以参考。