5

If I want to take a &str like "aeiou" and turn it into an iterator roughly equivalent to ["a", "e", "i", "o", "u"].iter(), what's the most idiomatic way to do it?

I've tried doing "aeiou".split("") which seemed idiomatic to me, but I got empty &strs at the beginning and end.

I've tried doing "aeiou".chars() but it got pretty ugly and unwieldy from there trying to turn the chars into &strs.

For the time being, I just typed out ["a", "e", "i", "o", "u"].iter(), but there's got to be an easier, more idiomatic way.

For context, I'm eventually going to be looping over each value and passing it into something like string.matches(vowel).count().

Here's my overall code. Maybe I went astray somewhere else.

fn string_list_item_count<'a, I>(string: &str, list: I) -> usize
where
    I: IntoIterator<Item = &'a str>,
{
    let mut num_instances = 0;

    for item in list {
        num_instances += string.matches(item).count();
    }

    num_instances
}

// snip

string_list_item_count(string, vec!["a", "e", "i", "o", "u"])

// snip

If I could make string_list_item_count accept the std::str::pattern::Pattern trait inside the iterator, I think that would make this function accept iterators of &str and char, but the Pattern trait is a nightly unstable API and I'm trying to avoid using those.

  • Does this answer your question? [How do I convert a string to a list of chars?](https://stackoverflow.com/questions/47829646/how-do-i-convert-a-string-to-a-list-of-chars) – asky Jan 05 '20 at 07:25
  • 1
    No, I'm looking to basically do the opposite. Turn an iterator of chars into an iterator of &strs. –  Jan 05 '20 at 07:58
  • 1
    How do you define character in the context of your question? ASCII character? Unicode codepoint? Grapheme cluster? ...? – Lukas Kalbertodt Jan 05 '20 at 09:52
  • That's a good point. And it's why I wish Pattern was stable so I didn't have to specify exactly one type. I wish I could use anything that .matches accepts. It's kind of odd to me that .matches is stable but its underlying trait is not. –  Jan 05 '20 at 15:36

3 Answers3

6

You can use split_terminator instead of split to skip the empty string at the end of the iterator. Additionally, if you skip the first element of the iterator, you get the result you want:

let iterator = "aeiou".split_terminator("").skip(1);
println!("{:?}", iterator.collect::<Vec<_>>());

Output:

["a", "e", "i", "o", "u"]
sshashank124
  • 31,495
  • 9
  • 67
  • 76
2

I would use str::split with an empty string and then remove any empty strings using Iterator::filter:

fn string_chars(s: &str) -> impl Iterator<Item = &str> {
    s.split("").filter(|s| !s.is_empty())
}

fn main() {
    assert_eq!(
        string_chars("aeiou").collect::<Vec<_>>(),
        ["a", "e", "i", "o", "u"],
    );
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
1

A closure can also serve as a Pattern:

fn main() {
    let vowels = "aeiou";
    let s = "the quick brown fox jumps over the lazy dog";
    let count = string_item_count(s, vowels);
    dbg!(count);
}

fn string_item_count(string: &str, pat: &str) -> usize {
    let pred = |c| pat.contains(c);
    string.matches(pred).count()
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
edwardw
  • 12,652
  • 3
  • 40
  • 51