Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get a slice from an Iterator?

Tags:

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.

like image 660
antoyo Avatar asked Oct 17 '15 17:10

antoyo


2 Answers

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.

like image 179
Matthieu M. Avatar answered Sep 18 '22 15:09

Matthieu M.


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

like image 44
Leonard Pauli Avatar answered Sep 20 '22 15:09

Leonard Pauli