【问题标题】:Mutate vector within filter过滤器内的变异向量
【发布时间】:2019-08-02 07:43:12
【问题描述】:

所以,我有以下代码在向量中成功执行过滤:

let mut v1 : Vec<i32> = vec!(1,2,3);
let v2 : Vec<&mut i32> = v1.iter_mut().filter(|x| {**x == 2}).collect();
println!("{:?}", v2);

由于过滤函数中谓词的类型签名是 FnMut(&amp;Self::Item) -&gt; bool,我假设里面的突变 关闭将起作用:

let mut v1 : Vec<i32> = vec!(1,2,3);
let v2 : Vec<&mut i32> = v1.iter_mut().filter(|x| {**x = 3; **x == 2}).collect();
println!("{:?}", v2);

但是上面的代码会导致编译错误。如何解决?笔记 我正在玩 rust 以获得更好的理解,所以 abpve 示例没有意义(通常,没有人会尝试变异 过滤器内的东西)。

【问题讨论】:

  • 没有人会尝试改变过滤器内的东西:所以这是不允许的。也许像.map(|x| *x = 3).filter(|x| *x == 2)这样的东西?
  • @rodrigo 我试图理解为什么不允许这样做。我虽然你可以用FnMut 做突变。
  • @Stargateur 但我可以使用map 进行变异,其类型为Self::Item,而不是mut
  • @Stargateur 谢谢,那种帮助!但我认为rust-lang 链接已损坏。您可以将其添加为带有固定链接的答案吗?

标签: rust


【解决方案1】:

您混淆了两个概念:FnMut 表示函数可以更改其捕获的变量,例如:

fn main() {
    let v1 = vec![1, 2, 3];
    let mut i = 0usize;
    let v2: Vec<_> = v1
        .into_iter()
        .filter(|x| {
            i = i + 1;
            *x == 2
        })
        .collect();
    println!("We iterate {} times and produce {:?}", i, v2);
}

这并不意味着函数的每个参数都是可变的。

在您的代码中,filter() 采用 &amp;Self::Item,这与采用 Self::Itemmap() 非常不同。因为真正的类型将转换为Map&lt;Item=&amp;mut i32&gt;Filter&lt;Item=&amp;&amp;mut i32&gt;。如果引用位于非可变引用后面,Rust 会禁止你改变引用:

fn test(a: &&mut i32) {
    **a = 5;
}
error[E0594]: cannot assign to `**a` which is behind a `&` reference

这是因为 Rust 遵循 the-rules-of-references:

  • 在任何给定时间,您都可以拥有一个可变引用或任意数量的不可变引用。
  • 引用必须始终有效。

这意味着您可以拥有多个&amp;&amp;mut,但只能拥有一个&amp;mut &amp;mut。如果 Rust 没有阻止您,您可以修改 &amp;&amp;mut,这会毒害任何其他 &amp;&amp;mut

很遗憾,E0594 的完整错误描述仍然不可用,请参阅#61137

注意:使用迭代器 API 时要避免副作用,我认为改变 FnMut 状态而不是项目是可以的,你应该在 for 循环中执行此操作,例如:

fn main() {
    let mut v1 = vec![1, 2, 3];
    for x in v1.iter_mut().filter(|x| **x == 2) {
        *x = 1;
    }
    println!("{:?}", v1);
}

【讨论】:

  • 我认为在第一个函数中你的意思是 iter_mut 而不是 into_iter...
  • 这不会改变任何事情
  • 为什么会这样?不是into_iter 它会遍历T,因此对于地图情况,实际类型将是Map&lt;Item=i32&gt;,而不是Map&lt;Item=&amp;mut i32&gt; 情况下的Map&lt;Item=&amp;mut i32&gt;
  • 是的,但在 filter() 中,T 仍将位于引用后面,因此 &amp;T 不可变。 play.integer32.com/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多