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
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
}
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)
}
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)
}
return
map
and max
Option::unwrap_or
to provide a default. 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
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.