How to get a slice from an Iterator?

17,466

Solution 1

So I wonder if there is a way to convert an Iterator to a slice

There is not.

An iterator only provides one element at a time, whereas a slice is about getting several elements at a time. This is why you first need to collect all the elements yielded by the Iterator into a contiguous array (Vec) before being able to use a slice.

The first obvious answer is not to worry about the slight overhead, though personally I would prefer placing the type hint next to the variable (I find it more readable):

let names: Vec<_> = args.arguments.iter().map(|arg| {
    arg.name.clone()
}).collect();
function(&names);

Another option would be for function to take an Iterator instead (and an iterator of references, at that):

let names = args.arguments.iter().map(|arg| &arg.name);
function(names);

After all, iterators are more general, and you can always "realize" the slice inside the function if you need to.

Solution 2

So I wonder if there is a way to convert an Iterator to a slice

There is. (in applicable cases)

Got here searching "rust iter to slice", for my use-case, there was a solution:

fn main() {
  // example struct
  #[derive(Debug)]
  struct A(u8);
  let list = vec![A(5), A(6), A(7)];
  // list_ref passed into a function somewhere ...
  let list_ref: &[A] = &list;
  let mut iter = list_ref.iter();
  // consume some ...
  let _a5: Option<&A> = iter.next();
  // now want to eg. return a slice of the rest
  let slice: &[A] = iter.as_slice();
  println!("{:?}", slice); // [A(6), A(7)]
}

That said, .as_slice is defined on an iter of an existing slice, so the previous answerer was correct in that if you've got, eg. a map iter, you would need to collect it first (so there is something to slice from).

docs: https://doc.rust-lang.org/std/slice/struct.Iter.html#method.as_slice

Share:
17,466
antoyo
Author by

antoyo

Updated on July 04, 2022

Comments

  • antoyo
    antoyo 5 months

    I started to use clippy as a linter. Sometimes, it shows this warning:

    writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be
    used with non-Vec-based slices. Consider changing the type to `&[...]`,
    #[warn(ptr_arg)] on by default
    

    I changed the parameter to a slice but this adds boilerplate on the call side. For instance, the code was:

    let names = args.arguments.iter().map(|arg| {
        arg.name.clone()
    }).collect();
    function(&names);
    

    but now it is:

    let names = args.arguments.iter().map(|arg| {
        arg.name.clone()
    }).collect::<Vec<_>>();
    function(&names);
    

    otherwise, I get the following error:

    error: the trait `core::marker::Sized` is not implemented for the type
    `[collections::string::String]` [E0277]
    

    So I wonder if there is a way to convert an Iterator to a slice or avoid having to specify the collected type in this specific case.