Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I process a range in slices in Rust?

Tags:

slice

vector

rust

I understand that the preferred way to iterate in Rust is through the for var in (range) syntax, but sometimes I'd like to work on more than one of the elements in that range at a time.

From a Ruby perspective, I'm trying to find a way of doing (1..100).each_slice(5) do |this_slice| in Rust.

I'm trying things like

for mut segment_start in (segment_size..max_val).step_by(segment_size) {
    let this_segment = segment_start..(segment_start + segment_size).iter().take(segment_size);
}

but I keep getting errors that suggest I'm barking up the wrong type tree. The docs aren't helpful either--they just don't contain this use case.

What's the Rust way to do this?

like image 701
bright-star Avatar asked May 04 '16 16:05

bright-star


People also ask

How do you slice an array in rust?

Rust - Slices 1 Syntax. The minimum index value is 0 and the maximum index value is the size of the data structure. NOTE that the end_index will not be included in final string. 2 Output 3 Illustration - Slicing an integer array. The main () function declares an array with 5 elements. ...

What is the use of slices in rust?

Slices are also present in Python which is similar to slice here in Rust. Slice is used when you do not want the complete collection, or you want some part of it. In slicing first element is at 0 index and the last is index-1. //gfg is String or Array &gfg [0..2] //from 0 to 1 index &gfg [0..3] //from 0 to 2 index

Why is rust so strict on array size?

In Rust, an array's size is part of the type. For example, this code will not compile: Rust's strictness also prevents problems like array to pointer decay in C/C++: //C++ code #include <iostream> using namespace std; //Looks can be deceiving: arr is not a pointer //to an array of 5 integers.

How do I create a slice with second and third elements?

For example if you have an array: You can create a slice containing second and third elements like this: The [1..3] syntax creates a range from index 1 (inclusive) to 3 (exclusive). If you omit the first number in the range ( [..3]) it defaults to zero and if you omit the last number ( [1..]) it defaults to the length of the array.


1 Answers

Use chunks (or chunks_mut if you need mutability):

fn main() {
    let things = [5, 4, 3, 2, 1];

    for slice in things.chunks(2) {
        println!("{:?}", slice);
    }
}

Outputs:

[5, 4]
[3, 2]
[1]

The easiest way to combine this with a Range would be to collect the range to a Vec first (which dereferences to a slice):

fn main() {
    let things: Vec<_> = (1..100).collect();

    for slice in things.chunks(5) {
        println!("{:?}", slice);
    }
}

Another solution that is pure-iterator would be to use Itertools::chunks_lazy:

extern crate itertools;

use itertools::Itertools;

fn main() {
    for chunk in &(1..100).chunks_lazy(5) {
        for val in chunk {
            print!("{}, ", val);
        }
        println!("");
    }
}

Which suggests a similar solution that only requires the standard library:

fn main() {
    let mut range = (1..100).peekable();

    while range.peek().is_some() {
        for value in range.by_ref().take(5) {
            print!("{}, ", value);
        }
        println!("");
    }
}

One trick is that Ruby and Rust have different handling here, mostly centered around efficiency.

In Ruby Enumerable can create new arrays to stuff values in without worrying about ownership and return a new array each time (check with this_slice.object_id).

In Rust, allocating a new vector each time would be pretty unusual. Additionally, you can't easily return a reference to a vector that the iterator holds due to complicated lifetime concerns.

A solution that's very similar to Ruby's is:

fn main() {
    let mut range = (1..100).peekable();

    while range.peek().is_some() {
        let chunk: Vec<_> = range.by_ref().take(5).collect();

        println!("{:?}", chunk);
    }
}

Which could be wrapped up in a new iterator that hides the details:

use std::iter::Peekable;

struct InefficientChunks<I>
    where I: Iterator
{
    iter: Peekable<I>,
    size: usize,
}

impl<I> Iterator for InefficientChunks<I>
    where I: Iterator
{
    type Item = Vec<I::Item>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.iter.peek().is_some() {
            Some(self.iter.by_ref().take(self.size).collect())
        } else {
            None
        }
    }
}

trait Awesome: Iterator + Sized {
    fn inefficient_chunks(self, size: usize) -> InefficientChunks<Self> {
        InefficientChunks {
            iter: self.peekable(),
            size: size,
        }
    }
}

impl<I> Awesome for I where I: Iterator {}

fn main() {
    for chunk in (1..100).inefficient_chunks(5) {
        println!("{:?}", chunk);
    }
}
like image 162
Shepmaster Avatar answered Sep 19 '22 10:09

Shepmaster