【问题标题】:Filter a vector without losing its ownership过滤向量而不丢失其所有权
【发布时间】:2021-06-17 23:56:29
【问题描述】:

给定一个结构如下:

#[derive(Debug)]
struct Item {
    id: u32
}

impl Item {
    fn new(id: u32) -> Item {
        Item { id }
    }
}

我正在寻找一种在不获取其所有权的情况下对该结构的向量执行过滤器的方法。以下代码将无法工作,因为所有权已被移动:

fn main() {
    let items: Vec<Item> = vec![Item::new(1), Item::new(2), Item::new(3)];
    let odd_items: Vec<Item> = items.into_iter()
            .filter(| item | item.id % 2 == 1)
            .collect();
    for i in odd_items.iter() { println!("{:?}", i); }

    for i in items.iter() { println!("{:?}", i); }
}

目前,我有两种解决方案:

  1. 有一个&amp;Item而不是Item的向量,但是,我发现从Vec&lt;Item&gt;开始但以Vec&lt;&amp;Item&gt;结束有点尴尬:
fn main() {
    let items: Vec<Item> = vec![Item::new(1), Item::new(2), Item::new(3)];
    let odd_items: Vec<&Item> = items.iter()
            .filter(| item | item.id % 2 == 1)
            .collect();
    for i in odd_items.iter() { println!("{:?}", i); }

    for i in items.iter() {  println!("{:?}", i); }
}
  1. 克隆一个初始向量。我更喜欢这个,但它会导致不必要的克隆,我更喜欢在过滤后只克隆项目:
fn main() {
    let items: Vec<Item> = vec![Item::new(1), Item::new(2), Item::new(3)];
    let odd_items: Vec<Item> = items.clone()
            .into_iter()
            .filter(| item | item.id % 2 == 1)
            .collect();
    for i in odd_items.iter() { println!("{:?}", i); }

    for i in items.iter() { println!("{:?}", i); }
}

我想知道是否有更好的方法可以在不丢失所有权的情况下过滤向量。

【问题讨论】:

    标签: rust


    【解决方案1】:

    克隆一个初始向量。我更喜欢这个,但它会导致不必要的克隆,我更喜欢只克隆过滤后的项目:

    如果你想在过滤后进行克隆,当然可以这样做,它会有将&amp;Item转换为Item的副作用(这就是clone()的定义):

    let items: Vec<Item> = vec![Item::new(1), Item::new(2), Item::new(3)];
    let odd_items: Vec<Item> = items
        .iter()
        .filter(|item| item.id % 2 == 1)
        .cloned()     // the same as .map(Item::clone)
        .collect();
    

    顺便说一句,如果您可以摆脱Vec&lt;&amp;Item&gt;,该解决方案可能会更有效,因为它使用的空间更少(也就是说,如果实际项目大于指针),并且它将odd_items 创建为“视图”进入items。如果这不是您想要的,那就去克隆吧,您只需将#[derive(Clone)] 添加到Item

    还要注意,尽管clone() 名声不好,但它并不一定很昂贵——“克隆”一个只包含一个u32struct 并不比一个u32 分配更昂贵。只有当类型很大和/或包含堆分配的数据时,才会避免克隆。

    【讨论】:

      【解决方案2】:

      在您的具体情况下,您实际上不需要调用collect,因为下一个for 循环可以直接使用迭代器:

      fn main() {
          let items: Vec<Item> = vec![Item::new(1), Item::new(2), Item::new(3)];
          let odd_items = items.iter().filter(|item| item.id % 2 == 1);
      
          for i in odd_items {
              println!("{:?}", i);
          }
      
          for i in items.iter() {
              println!("{:?}", i);
          }
      }
      

      使用这种技术,odd_items 是对 &amp;Item 的迭代器的绑定,但类型推断隐藏了它(实际类型更复杂,因为链接迭代器通常会产生类似 Filter&lt;Map&lt;Iter&lt;...&gt;&gt; 等的东西)。

      这样做的主要好处是除了items之外没有分配Vec,而且后者保留所有权。

      Playground link

      【讨论】:

        猜你喜欢
        • 2015-07-07
        • 2021-07-27
        • 2012-12-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-08
        • 2018-04-19
        相关资源
        最近更新 更多