4

I'm trying to write a function that processes a sequence of integers.

fn process_one(n: u32) {}

fn process<II>(ii: II)
where
    II: IntoIterator<Item = u32>,
{
    for n in ii {
        process_one(n);
    }
}

I want the client to be able to pass a Vec<u32> without consuming it (process(&v)). This function can't be used because <&Vec<u32> as IntoIterator>::Item is &u32; I'd have to pass v.iter().cloned() instead, which is annoying.

Alternatively, I could make the bound Item = &u32 and use process_one(*n), but then I have the reverse problem.

I'm trying to think of a way to write this generically, but I can't figure out how. As far as I can tell, none of AsRef, Borrow, ToOwned, or Deref work.

What I need is a way to write this:

fn process<II>(ii: II)
where
    II: IntoIterator<Item = MAGIC>, /* MORE MAGIC */
{
    for n in ii {
        process_one(MAGIC(n));
    }
}

so that all of these compile:

fn test() {
    let v: Vec<u32> = vec![1, 2, 3, 4];
    process(&v);
    process(v);
    process(1..10);
}

I know I can do this using a custom trait, but I feel like there should be a way without all that boilerplate.

trent
  • 25,033
  • 7
  • 51
  • 90
Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • Possible duplicate of [How do I create a function that accepts an iterator of i32s as either values or references and sums them?](https://stackoverflow.com/questions/46288670/how-do-i-create-a-function-that-accepts-an-iterator-of-i32s-as-either-values-or) – ljedrz Jan 18 '18 at 09:37
  • The question is similar, but the key difference is that the person there just wants to sum things up and `Add` is implemented for references, while I need to pass the value on to a function that simply takes an `u32`. – Sebastian Redl Jan 18 '18 at 09:44
  • If you take things by value, this is a consuming iterator. Thus in order to consume a vector, you must own it, not borrow it. You have incompatible requirements. – Boiethios Jan 18 '18 at 09:48
  • @Boiethios I want the function to be agnostic to consuming or not consuming the sequence. – Sebastian Redl Jan 18 '18 at 09:49
  • @SebastianRedl ah, ok; I'll retract my close vote, but you may want to adjust the question so that it doesn't come back from someone else :). – ljedrz Jan 18 '18 at 09:53
  • Ah, I understand: the problem is in `process_one`... But the question is still unclear for me. – Boiethios Jan 18 '18 at 09:54
  • @Boiethios Is it clearer now what I want? Note that I can't change `process_one`. – Sebastian Redl Jan 18 '18 at 10:10
  • You do not have to own the vector, because `u32` can be cloned, so why do you not write: `fn process(slice: &[u32])`? Furthermore, there is no overhead when cloning an `u32`. If the user does not want his vector anymore after that, he does not have to use it. – Boiethios Jan 18 '18 at 10:16
  • @Boiethios That wouldn't work with other iterables, e.g. a range `1..42u32`, which is `IntoIterator`. I'm also not worried about performance overhead, I'm worried about syntactic overhead. – Sebastian Redl Jan 18 '18 at 10:18

2 Answers2

10

Borrow works:

use std::borrow::Borrow;

fn main() {
    let x = vec![1, 2, 3];
    process(x.iter());
    process(x);
    process(1..3);
}

fn process_one(n: u32) {
    println!("{}", n)
}

fn process<I>(iter: I)
where
    I: IntoIterator,
    I::Item: Borrow<u32>,
{
    for x in iter {
        process_one(*x.borrow());
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
raggy
  • 215
  • 2
  • 7
2

Here's how I would do it with a custom trait.

trait ToValue<V> {
    fn to_value(self) -> V;
}

impl<T> ToValue<T> for T {
    fn to_value(self) -> T {
        self
    }
}

impl<'a, T> ToValue<T> for &'a T
where
    T: Copy,
{
    fn to_value(self) -> T {
        *self
    }
}

fn process<N, II>(ii: II)
where
    II: IntoIterator<Item = N>,
    N: ToValue<u32>,
{
    for n in ii {
        process_one(n.to_value());
    }
}

While this solves my problem, it leaves me rather unsatisfied, and I still think there should be a solution that doesn't involve my own trait.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • It would be useful if compiler had exposed automatic dereference functionality. Something like this: [playground](https://play.rust-lang.org/?gist=b3f7edd5890c61f5b0a24a9968158b73&version=stable) – red75prime Jan 18 '18 at 11:29
  • This is essentially `ToOwned` but limited to `Copy` types, right? – trent Jan 18 '18 at 16:25
  • Yes, but `ToOwned` has an internal `type Owned: Borrow` requirement, which made it impossible to use for me. – Sebastian Redl Jan 18 '18 at 18:26