简体   繁体   中英

Why can't I use `&Iterator<Item = &String>` as an iterator?

I have the following function that's supposed to find and return the longest length of a String given an Iterator :

fn max_width(strings: &Iterator<Item = &String>) -> usize {
    let mut max_width = 0;
    for string in strings {
        if string.len() > max_width {
            max_width = string.len();
        }
    }
    return max_width;
}

However, the compiler gives me the following error:

error[E0277]: the trait bound `&std::iter::Iterator<Item=&std::string::String>: std::iter::Iterator` is not satisfied
 --> src/main.rs:3:19
  |
3 |     for string in strings {
  |                   ^^^^^^^ `&std::iter::Iterator<Item=&std::string::String>` is not an iterator; maybe try calling `.iter()` or a similar method
  |
  = help: the trait `std::iter::Iterator` is not implemented for `&std::iter::Iterator<Item=&std::string::String>`
  = note: required by `std::iter::IntoIterator::into_iter`

I'm new to Rust, and terribly confused by this, since I thought I was explicitly passing in an iterator. Calling strings.iter() tells me that it is not implemented, and calling strings.into_iter() sends me down a mutability rabbit-hole, and I certainly don't want to mutate the passed argument.

How can I iterate over my strings?

Your code fails because Iterator is not the same as &Iterator . You can fix this if you pass Iterator to your function, but since Iterator is a trait, the size cannot be determined (You can't know, what Iterator you are passing). The solution is to pass anything that implements Iterator :

fn max_width<'a>(strings: impl Iterator<Item = &'a String>) -> usize

playground


For more experienced Rust users:

The most generic way is probably this:

fn max_width<T: AsRef<str>>(strings: impl IntoIterator<Item = T>) -> usize {
    let mut max_width = 0;
    for string in strings {
        let string = string.as_ref();
        if string.len() > max_width {
            max_width = string.len();
        }
    }
    max_width
}

playground

However, you can also use

fn max_width<T: AsRef<str>>(strings: impl IntoIterator<Item = T>) -> usize {
    strings
        .into_iter()
        .map(|s| s.as_ref().len())
        .max()
        .unwrap_or(0)
}

playground

The other answers show you how to accept an iterator, but gloss over answering your actual question:

Why can't I use &Iterator<Item = &String> as an iterator?

Amusingly enough, you've prevented it yourself:

and I certainly don't want to mutate the passed argument

Iterators work by mutating their target — that's how the iterator can change to return a new value for each call!

pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
    //       ^^^
}

By taking in an immutable trait object , it's impossible for your iterator to update itself, thus it's impossible to actually iterate.

The absolute smallest thing you can do to make your code compile is to accept a mutable reference:

fn max_width(strings: &mut dyn Iterator<Item = &String>) -> usize

However, I'd probably write the function as:

fn max_width<I>(strings: I) -> usize
where
    I: IntoIterator,
    I::Item: AsRef<str>,
{
    strings
        .into_iter()
        .map(|s| s.as_ref().len())
        .max()
        .unwrap_or(0)
}
  1. Don't use an explicit return
  2. Use iterator combinators like map and max
  3. Use Option::unwrap_or to provide a default.
  4. Use IntoIterator to accept anything that can be made into an iterator.

If you don't specifically need the generality of iterating over any given iterator, a simpler way to write the function is to have your max_width function take a &[&str] (A slice of string slices) instead. You can use a slice in a for loop because Rust knows how to turn that into an iterator (it implements IntoIterator trait):

fn max_width(strings: &[&str]) -> usize {
    let mut max_width = 0;
    for string in strings {
        if string.len() > max_width {
            max_width = string.len();
        }
    }
    return max_width;
}

fn main() {
    let strings = vec![
        "following",
        "function",
        "supposed",
        "return",
        "longest",
        "string",
        "length"
    ];

    let max_width = max_width(&strings);

    println!("Longest string had size {}", max_width);
}

// OUTPUT: Longest string had size 9

Playground here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM