0

The following code works:

fn do_it_fun<I>(words: Vec<&str>, inputs: I) 
    where I: AsRef<[&'static str]>
    {
    for word in words {
        if inputs.as_ref().into_iter().any(|i| *i == word) {
            println!("Match: {}", word);
        }
    }
}

fn main() {
    let input = vec!["foo", "bar"];
    let input2 = ["bar", "baz"];
    do_it_fun(input, input2);
}

However, instead of trying to use a slice type, if I just use Iterator, the compiler tells me to use dynamic polymorphism:

fn do_it_fun<I>(words: Vec<&str>, inputs: I) 
    where I: AsRef<IntoIterator<Item=&'static str>>
    {
    for word in words {
        if inputs.as_ref().into_iter().any(|i| *i == word) {
            println!("Match: {}", word);
        }
    }
}

fn main() {
    let input = vec!["foo", "bar"];
    let input2 = ["bar", "baz"];
    do_it_fun(input, input2);
}
   Compiling playground v0.0.1 (/playground)
error[E0782]: expected a type, found a trait
 --> src/main.rs:2:20
  |
2 |     where I: AsRef<IntoIterator<Item=&'static str>>
  |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
help: you can add the `dyn` keyword if you want a trait object
  |
2 |     where I: AsRef<dyn IntoIterator<Item=&'static str>>
  |                    +++

For more information about this error, try `rustc --explain E0782`.
error: could not compile `playground` (bin "playground") due to 1 previous error

Any ways around this? Using a generic IntoIterator but retaining the possibility of iterating over its references?

2
  • Even if you manage the syntax to express what you want, the result won't be ergonomic since the intermediate iterator type would be unconstrained - a type can implement AsRef<T> for multiple Ts and there are multiple types that can implement Iterator<Item = &'static str> which the compiler won't attempt to reconcile (it would ask the caller to explicitly specify what that type is). Commented May 12 at 17:39
  • Also iterating over a &[&'static str] gives you &&'static str and not &'static str. Commented May 12 at 17:40

2 Answers 2

1

It seems to me that the "generalized" version of AsRef<[_]> is NOT AsRef<IntoIterator<Item=_>> but rather simply IntoIterator<Item=_>. In your original example the AsRef serves to allow accepting things which dereference to slices, in the generalized version, IntoIterator servers to allowing accepting things which convert to Iterators.

You can also generalize from &'static str to any type from which you can borrow an str, since you only use the str to compare to other strings.

To answer the question in the title, you cannot iterate over an arbitrary iterator multiple times. The typical way to express that an iterator is "reusable" in this was is to use Clone, because a Cloned iterator keeps its original state.

fn do_it_fun<I, X>(words: Vec<&str>, inputs: I) 
    where I: IntoIterator<Item=X>,
          X : Borrow<str>,
          <I as IntoIterator>::IntoIter : Clone
{
    let local_inputs = inputs.into_iter();
    for word in words {
        if local_inputs.clone().any(|i| i.borrow() == word) {
            println!("Match: {}", word);
        }
    }
}



fn main() {
    let input = vec!["foo", "bar"];
    let input2 = ["bar", "baz"];
    
    do_it_fun(input, input2);

    // most typical iterators like map, filter, etc. will be valid here
    // do_it_fun(input, input2.map(|x| x.to_uppercase()) );
}

Finally, you could also generalize the type of the 1st argument, although I'm not sure if this is useful for you:


fn do_it_fun_2<I, I2, X, X2>(words: I2, inputs: I) 
    where I: IntoIterator<Item=X>,
          X : Borrow<str>,
          <I as IntoIterator>::IntoIter : Clone,
          I2 : IntoIterator<Item=X2>,
          X2 : Borrow<str>,
{
    let local_inputs = inputs.into_iter();
    for word in words {
        let word = word.borrow();
        if local_inputs.clone().any(|i| i.borrow() == word) {
            println!("Match: {}", word);
        }
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Doesn't clone() have an additional overhead over just taking references via as_ref()?
It depends entirely on which iterator you are cloning. If the iterator is the one produced by calling into_iter() on a slice, the cost of clone is trivial (it's just copying pointers). Some iterators might have more expensive clone methods if they have complicated internal state. Other iterators are not cloneable at all.
0

The Rust Compiler states that it's 'wrong' because you're writing a function that accepts a generic response using an iterator.

This appears in your second/middle script, specifically where I: AsRef<IntoIterator<Item=&'static str>>. The problem is IntoIterator. It's a trait, and not a type, so you can't use a trait in generic (specifically AsRef) unless it's part of a dyn prefix. So the Compiler questions it because it's a trait and not a type.

If you wanted to fix this on itself, you'd use the dyn prefix, like dyn IntoIterator. But, it's not Trait-object-safe and doesn't support what I'd assume you want (If it does, you could use it).

So, instead of using dyn IntoIterator, just use IntoIterator and remove the AsRef reference. Instead of where I: AsRef<Iterator<Item=&'static str>>, replace it with where I: IntoIterator<Item = &'static str>. This should fix your problem and retain what you need.

If it doesn't, let me know so I can find the solution you want.

2 Comments

Just using IntoIterator won't work because of ownership / move semantics: play.rust-lang.org/…
@gust I see, not sure any other solutions then. It was just a thought that came to me that would possibly work. Hopefully another person can answer and find a solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.