Search code examples
rustvectoriterator

How to convert iter.take() to Vec<T> or to &[T]?


I have a struct called Cell:

pub struct Cell {
    x: X, // Some other struct
    y: Y, // Some other struct
    weight: usize,
}

I was trying to select the top preference cell out of some Row (a collection of Cells):

// Return the top n-matching cells with a positive weight
pub fn select_preference(&mut self) -> Vec<Cell> {
    let top = 3;

    self.sort();
    // After sorting, omit the cells with weight = 0
    // And select the top preference cells
    self.cells.split(|cell| cell.weight() == 0).take(top)
}

However, I am getting an expected error actually:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/lib.rs:35:9
   |
29 |     pub fn select_preference(&mut self) -> Vec<Cell> {
   |                                            --------- expected `Vec<Cell>` because of return type
...
35 |         self.cells.split(|cell| cell.weight() == 0).take(top)
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Vec`, found struct `std::iter::Take`
   |
   = note: expected struct `Vec<Cell>`
              found struct `std::iter::Take<std::slice::Split<'_, Cell, [closure@src/lib.rs:35:26: 35:32]>>`

I don't know how to convert the Take into Vec<Cell> or &[Cell]. I know the Take is some sort of Iterator but I'm unable to convert it.

Rust Playground


Solution

  • First, split is probably not what you want -- that creates an iterator where each element is a block of nonzero items. You probably want .iter().filter(|cell| cell.weight() != 0): iterate over elements of the vector, then filter out those that are nonzero.

    To return a vector from an iterator, you need .collect(). However, this would give a Vec<&Cell> -- which doesn't quite match your function signature. Since you want a Vec<Cell>, you also need to clone the elements first to get new cells -- so you can use .cloned() first. That requires adding #[derive(Clone)] to Cell. This is the end result:

    #[derive(Clone)]
    pub struct Cell {
        x: X,
        y: Y,
        weight: usize,
    }
    
    // Return the top n-matching cells with a positive weight
    pub fn select_preference(&mut self) -> Vec<Cell> {
        let top = 3;
    
        self.sort();
        // After sorting, omit the cells with weight = 0
        // And select the top preference cells
        self.cells.iter().filter(|cell| cell.weight() != 0).take(top).cloned().collect()
    }
    

    As a general rule, it's common to always derive Clone for structs of data.

    Other designs are possible too -- you can return the Vec<&Cell> directly, as the other answer suggests. Finally, you could return an iterator instead of a Vec; here's how that looks:

    pub fn select_preference(&mut self) -> impl Iterator<Item = &Cell> {
        let top = 3;
    
        self.sort();
        // After sorting, omit the cells with weight = 0
        // And select the top preference cells
        self.cells.iter().filter(|cell| cell.weight() != 0).take(top)
    }