使用 filter_map 的生命周期错误
Lifetime Errors using filter_map
我试图在 Rust 中将 Iterator 的 filter_map
函数与 HashMap
一起使用,但我无法编译它。假设我有一个 HashMap
和一个键列表。对于每个键,如果映射包含键,我会改变映射中的相应值。例如,假设值是 i32
类型,我想增加适当的值。
use std::collections::HashMap;
fn increment(map: &mut HashMap<i32, i32>, keys: &[i32]) {
for value in keys.iter().filter_map(|index| map.get_mut(index)) {
*value += 1;
}
}
fn main() {
let mut map = HashMap::new();
map.insert(1,2);
map.insert(4,5);
increment(&mut map, &[0, 1, 2]);
assert!(*map.get(&1).unwrap() == 3);
assert!(*map.get(&4).unwrap() == 5);
}
这段代码给我一个与生命周期相关的错误:
<anon>:4:57: 4:71 error: cannot infer an appropriate lifetime for autoref due to conflicting requirements
<anon>:4 for value in keys.iter().filter_map(|index| map.get_mut(index)) {
^~~~~~~~~~~~~~
<anon>:4:9: 6:10 note: in this expansion of for loop expansion
<anon>:4:9: 6:10 note: first, the lifetime cannot outlive the call at 4:8...
<anon>:4 for value in keys.iter().filter_map(|index| map.get_mut(index)) {
<anon>:5 *value += 1;
<anon>:6 }
<anon>:4:9: 6:10 note: in this expansion of for loop expansion
<anon>:4:9: 6:10 note: ...so that argument is valid for the call
<anon>:4 for value in keys.iter().filter_map(|index| map.get_mut(index)) {
<anon>:5 *value += 1;
<anon>:6 }
<anon>:4:9: 6:10 note: in this expansion of for loop expansion
<anon>:4:53: 4:71 note: but, the lifetime must be valid for the method call at 4:52...
<anon>:4 for value in keys.iter().filter_map(|index| map.get_mut(index)) {
^~~~~~~~~~~~~~~~~~
<anon>:4:9: 6:10 note: in this expansion of for loop expansion
<anon>:4:53: 4:56 note: ...so that method receiver is valid for the method call
<anon>:4 for value in keys.iter().filter_map(|index| map.get_mut(index)) {
^~~
<anon>:4:9: 6:10 note: in this expansion of for loop expansion
error: aborting due to previous error
为什么我会收到此错误,使用惯用 Rust 处理这种情况的最佳方法是什么?
我不知道你为什么要把 filter_map
带入这里。简单地遍历键并设置值对我来说更加明显:
fn increment(map: &mut HashMap<i32, i32>, keys: &[i32]) {
for index in keys {
if let Some(value) = map.get_mut(index) {
*value += 1;
}
}
}
您的第一个解决方案有一个大问题 — 如果您 两次使用相同的密钥 会怎样?由于您正在生成可变引用,因此您将拥有一个迭代器,该迭代器会两次分发相同的可变引用,从而引入 aliasing。这在安全的 Rust 中是被禁止的。
我认为这是导致您出现错误消息的原因,但我承认并没有完全将其归因于该问题。我知道它最终会导致问题。
I don't see the aliasing effect. We are producing mutable references with get_mut
, but the lifetimes of these references should not overlap as far as I can tell. Unless I'm missing something, it follows the same logic as your code.
为了使它更明显,这是您的相同代码,但添加了 collect
:
let xs: Vec<_> = keys.iter().filter_map(|index| map.get_mut(index)).collect();
重要的内部部分是相同的 - 您正在生成一个 可能 包含对同一项目的多个可变引用的迭代器。
请注意,Rust 编译器无法(或至少不会)完全分析您的程序以查看在这种情况下您消耗了一个值并将其丢弃,从不持有到多个。它所能做的就是看到你所做的部分可能导致这种情况并阻止你这样做。
相反,上面的版本 永远不会 导致别名,因为可变引用不会持续超过 for
块。
I've desugared your original code†以获得更精确的误差。我最终在以下方法中遇到错误:
impl<'a, 'b> FnMut<(&'b i32,)> for MyClosure<'a> {
extern "rust-call"
fn call_mut(&mut self, (index,): (&'b i32,)) -> Option<&'a mut i32> {
self.map.get_mut(index)
}
}
错误是:
<anon>:21:18: 21:32 error: cannot infer an appropriate lifetime for autoref due to conflicting requirements [E0495]
<anon>:21 self.map.get_mut(index)
^~~~~~~~~~~~~~
通常,当您 return 来自函数的可变引用时,它会绑定到参数的生命周期。 returned 引用导致作为参数传递给函数的值仍然被认为是借用的,并且根据 Rust 的规则,在第一次借用超出范围之前,您不能对该值进行另一次借用。因此,该程序无法编译:
struct Foo {
x: i32
}
impl Foo {
fn x_mut(&mut self) -> &mut i32 { &mut self.x }
}
fn main() {
let mut foo = Foo { x: 0 };
let a = foo.x_mut();
foo.x_mut(); // error: cannot borrow `foo` as mutable more than once at a time
}
问题是您正在尝试 return 具有生命周期 'a
的可变引用,但该生命周期并未正确表达您实际上是从 [=16= 借用的事实].因此,编译器不会考虑调用后借用的 MyClosure
并允许您再次调用闭包,这可能 return 一个与之前 returned 相同的可变引用,导致可变引用的别名,这在安全的 Rust 中是被禁止的。
为此,FnMut
实现必须这样写:
impl<'a, 'b> FnMut<(&'b i32,)> for MyClosure<'a> {
extern "rust-call"
fn call_mut<'c>(&'c mut self, (index,): (&'b i32,)) -> Option<&'c mut i32> {
self.map.get_mut(index)
}
}
但这是无效的:
<anon>:19:5: 22:6 error: method `call_mut` has an incompatible type for trait:
expected bound lifetime parameter ,
found concrete lifetime [E0053]
<anon>:19 extern "rust-call"
<anon>:20 fn call_mut<'c>(&'c mut self, (index,): (&'b i32,)) -> Option<&'c mut i32> {
<anon>:21 self.map.get_mut(index)
<anon>:22 }
这与尝试编写流式迭代器时生成的错误相同。
† 实际上,这个脱糖代码对应于闭包move |index| map.get_mut(index)
。您的原始闭包将包含一个 &mut &mut HashMap<i32, i32>
字段,而不是 &mut HashMap<i32, i32>
字段。
我试图在 Rust 中将 Iterator 的 filter_map
函数与 HashMap
一起使用,但我无法编译它。假设我有一个 HashMap
和一个键列表。对于每个键,如果映射包含键,我会改变映射中的相应值。例如,假设值是 i32
类型,我想增加适当的值。
use std::collections::HashMap;
fn increment(map: &mut HashMap<i32, i32>, keys: &[i32]) {
for value in keys.iter().filter_map(|index| map.get_mut(index)) {
*value += 1;
}
}
fn main() {
let mut map = HashMap::new();
map.insert(1,2);
map.insert(4,5);
increment(&mut map, &[0, 1, 2]);
assert!(*map.get(&1).unwrap() == 3);
assert!(*map.get(&4).unwrap() == 5);
}
这段代码给我一个与生命周期相关的错误:
<anon>:4:57: 4:71 error: cannot infer an appropriate lifetime for autoref due to conflicting requirements
<anon>:4 for value in keys.iter().filter_map(|index| map.get_mut(index)) {
^~~~~~~~~~~~~~
<anon>:4:9: 6:10 note: in this expansion of for loop expansion
<anon>:4:9: 6:10 note: first, the lifetime cannot outlive the call at 4:8...
<anon>:4 for value in keys.iter().filter_map(|index| map.get_mut(index)) {
<anon>:5 *value += 1;
<anon>:6 }
<anon>:4:9: 6:10 note: in this expansion of for loop expansion
<anon>:4:9: 6:10 note: ...so that argument is valid for the call
<anon>:4 for value in keys.iter().filter_map(|index| map.get_mut(index)) {
<anon>:5 *value += 1;
<anon>:6 }
<anon>:4:9: 6:10 note: in this expansion of for loop expansion
<anon>:4:53: 4:71 note: but, the lifetime must be valid for the method call at 4:52...
<anon>:4 for value in keys.iter().filter_map(|index| map.get_mut(index)) {
^~~~~~~~~~~~~~~~~~
<anon>:4:9: 6:10 note: in this expansion of for loop expansion
<anon>:4:53: 4:56 note: ...so that method receiver is valid for the method call
<anon>:4 for value in keys.iter().filter_map(|index| map.get_mut(index)) {
^~~
<anon>:4:9: 6:10 note: in this expansion of for loop expansion
error: aborting due to previous error
为什么我会收到此错误,使用惯用 Rust 处理这种情况的最佳方法是什么?
我不知道你为什么要把 filter_map
带入这里。简单地遍历键并设置值对我来说更加明显:
fn increment(map: &mut HashMap<i32, i32>, keys: &[i32]) {
for index in keys {
if let Some(value) = map.get_mut(index) {
*value += 1;
}
}
}
您的第一个解决方案有一个大问题 — 如果您 两次使用相同的密钥 会怎样?由于您正在生成可变引用,因此您将拥有一个迭代器,该迭代器会两次分发相同的可变引用,从而引入 aliasing。这在安全的 Rust 中是被禁止的。
我认为这是导致您出现错误消息的原因,但我承认并没有完全将其归因于该问题。我知道它最终会导致问题。
I don't see the aliasing effect. We are producing mutable references with
get_mut
, but the lifetimes of these references should not overlap as far as I can tell. Unless I'm missing something, it follows the same logic as your code.
为了使它更明显,这是您的相同代码,但添加了 collect
:
let xs: Vec<_> = keys.iter().filter_map(|index| map.get_mut(index)).collect();
重要的内部部分是相同的 - 您正在生成一个 可能 包含对同一项目的多个可变引用的迭代器。
请注意,Rust 编译器无法(或至少不会)完全分析您的程序以查看在这种情况下您消耗了一个值并将其丢弃,从不持有到多个。它所能做的就是看到你所做的部分可能导致这种情况并阻止你这样做。
相反,上面的版本 永远不会 导致别名,因为可变引用不会持续超过 for
块。
I've desugared your original code†以获得更精确的误差。我最终在以下方法中遇到错误:
impl<'a, 'b> FnMut<(&'b i32,)> for MyClosure<'a> {
extern "rust-call"
fn call_mut(&mut self, (index,): (&'b i32,)) -> Option<&'a mut i32> {
self.map.get_mut(index)
}
}
错误是:
<anon>:21:18: 21:32 error: cannot infer an appropriate lifetime for autoref due to conflicting requirements [E0495]
<anon>:21 self.map.get_mut(index)
^~~~~~~~~~~~~~
通常,当您 return 来自函数的可变引用时,它会绑定到参数的生命周期。 returned 引用导致作为参数传递给函数的值仍然被认为是借用的,并且根据 Rust 的规则,在第一次借用超出范围之前,您不能对该值进行另一次借用。因此,该程序无法编译:
struct Foo {
x: i32
}
impl Foo {
fn x_mut(&mut self) -> &mut i32 { &mut self.x }
}
fn main() {
let mut foo = Foo { x: 0 };
let a = foo.x_mut();
foo.x_mut(); // error: cannot borrow `foo` as mutable more than once at a time
}
问题是您正在尝试 return 具有生命周期 'a
的可变引用,但该生命周期并未正确表达您实际上是从 [=16= 借用的事实].因此,编译器不会考虑调用后借用的 MyClosure
并允许您再次调用闭包,这可能 return 一个与之前 returned 相同的可变引用,导致可变引用的别名,这在安全的 Rust 中是被禁止的。
为此,FnMut
实现必须这样写:
impl<'a, 'b> FnMut<(&'b i32,)> for MyClosure<'a> {
extern "rust-call"
fn call_mut<'c>(&'c mut self, (index,): (&'b i32,)) -> Option<&'c mut i32> {
self.map.get_mut(index)
}
}
但这是无效的:
<anon>:19:5: 22:6 error: method `call_mut` has an incompatible type for trait:
expected bound lifetime parameter ,
found concrete lifetime [E0053]
<anon>:19 extern "rust-call"
<anon>:20 fn call_mut<'c>(&'c mut self, (index,): (&'b i32,)) -> Option<&'c mut i32> {
<anon>:21 self.map.get_mut(index)
<anon>:22 }
这与尝试编写流式迭代器时生成的错误相同。
† 实际上,这个脱糖代码对应于闭包move |index| map.get_mut(index)
。您的原始闭包将包含一个 &mut &mut HashMap<i32, i32>
字段,而不是 &mut HashMap<i32, i32>
字段。