【问题标题】:How can I remove members of a collection type in place?如何删除集合类型的成员?
【发布时间】:2019-10-01 07:40:04
【问题描述】:

我想遍历结构中的集合类型并删除一些值,但 Rust 阻止我销毁集合:

fn some_method(&mut self) {
    self.collection = self
        .collection
        .into_iter()
        .filter(/* ... */
        .collect();
}

我可以克隆所有值来构建另一个集合,但这并不高效。在 Rust 中从集合中删除值的惯用方式是什么?

【问题讨论】:

  • 如果filter 回调在中途发生恐慌,那么self.collection 应该是什么?请提供一个更完整的示例 sn-p,例如,collectionVec?如果是,那么 .drain(..) 而不是 into_iter() 将在这里工作,因为它不具有所有权。还有.retain(...)
  • @loganfsmyth 在我的例子中,集合是BTreeMap,它没有drain 方法。我真的需要关心恐慌吗?在 rust 中,恐慌是真正的恐慌,而不是应该正确处理的控制流?
  • @loganfsmyth 而我想要的是“在迭代时删除”,类似于 Java 中的 iterator.remove()
  • @JiamingLu Rust 的迭代器可以迭代不可变的东西。 Java 的迭代器可以做到这一点,如果可以,remove 会做什么?

标签: rust


【解决方案1】:

一个完整的例子(还有into_iter):

#[derive(Debug)]
struct Scores {
    collection: Vec<i32>,
}

impl Scores {
    fn new() -> Scores {
        return Scores {
            collection: Vec::new(),
        };
    }

    fn filter_in_above_50(&mut self) {
        self.collection = self
            .collection
            .drain(..)
            .filter(|score| score > &50)
            .collect();
    }

    fn filter_in_above_50_using_into_iter(&mut self) {
        let coll: &mut Vec<i32> = self.collection.as_mut();
        let coll: Vec<i32> = coll
            .into_iter()
            .filter(|score| score > &&mut 50i32)
            .map(|&mut x| x)
            .collect();
        self.collection = coll;
    }
}

还有测试:

#[test]
fn score_test() {
    let mut s = Scores::new();
    s.collection.push(199);
    s.collection.push(11);
    s.filter_in_above_50();
    assert_eq!(s.collection, vec![199]);
}

#[test]
fn score_test_using_into_iter() {
    let mut s = Scores::new();
    s.collection.push(199);
    s.collection.push(11);
    s.filter_in_above_50_using_into_iter();
    assert_eq!(s.collection, vec![199]);
}

【讨论】:

  • 在测试中打印一些东西而不是断言是个好主意吗?
  • 不是真的,解决了这个问题!感谢您强调:)
  • 但是drain只对Vec有效,BTreeMapHashMap没有这种方法
  • @JiamingLu,您的具体情况是什么?这个答案对 Vec 来说似乎很方便。如果您将问题编辑得更具体,您可以获得更好、更具体的答案。
  • filter_in_above_50_using_into_iter 不在Vec 上使用into_iter;它只是在&amp;mut Vec 上使用into_iter,这在非通用上下文中很奇怪(您可以使用.iter_mut() 来更具表现力和简洁)。它还要求TCopy。总的来说,这有点奇怪和单调。我不建议编写这样的代码。 (drain(..) 版本看起来不错。)
【解决方案2】:

由于collectionBTreeMap 并且没有.drain().retain,并且您可以在处理期间清空树,所以要做的事情是将树移出@987654326 @,随心所欲地对其进行操作,然后将其放回原处。

如您所见,Rust 不允许使用简单的赋值,因为如果在您的 sn-p 运行期间出现恐慌,self.collection 将处于不一致的状态。相反,您需要使用 std::mem::replace 显式执行此操作,这允许您通过提供替换来获取可变引用中内容的所有权,因此引用继续指向有效数据。

fn some_method(&mut self) {
  self.collection = std::mem::replace(&mut self.collection, BTreeMap::new())
    .into_iter()
    .filter(|_| true)
    .collect();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-20
    • 2021-11-07
    • 1970-01-01
    相关资源
    最近更新 更多