Lēctia Landau
Lēctia Landau

Reputation: 573

How to chain arbitrary number of iterables?

I understand some of the jank involving iterables and arrays, but clearly not enough. I want to take any amount of iterables (vectors, arrays, slices, anything implementing IntoIterator) and provide an expected final size, and get an array (i.e. fixed-size) containing the chained values. To clarify, this is mostly for easy refactoring and function calling, so I want this utility to take ownership of the passed iterables and move all their contents into its output, such that:

let a1: u8 = [1, 2, 3];
let a2: u8 = [4, 5, 6];
let joined = join::<u8, 6>([a1, a2, ...]); // [u8; 6]

I tried implementing something with chain, but couldn't get it to work out. I know I can do this unsafely, but I'd rather avoid that if possible. Is there a way to do what I want?

My best (non-working) attempt:

fn join<T, C: IntoIterator<Item = T>, const N: usize>(iterables: Vec<C>) -> [T; N] {
  let mut a = vec![].iter().chain(vec![]);
  for iterable in iterables {
    a = a.chain(iterable.into_iter());
  }
  a.collect().try_into().unwrap()
}

Upvotes: 0

Views: 318

Answers (1)

Lēctia Landau
Lēctia Landau

Reputation: 573

With credit to @SvenMarnach for simplifying, this problem is neatly solved like so:

use std::convert::TryInto;

fn join<T: Clone, const N: usize>(iterables: Vec<&[T]>) -> [T; N] {
  let slice = iterables.concat();
  let length = slice.len();
  slice.try_into()
    .unwrap_or_else(|_| panic!("joined has length {}, expected {}", length, N))
}

Used like so:

fn main() {
  let params1 = [1, 2, 3];
  let params2 = [4, 5];
  print!("sum: {}", sum_six_numbers(join(vec![&params1, &params2])));
}

fn sum_six_numbers(ns: [u8; 5]) -> u8 {
  ns.iter().sum()
}

Upvotes: 1

Related Questions