I am trying to make a function that will do the following:
input: a String
of arbitrary length of the form "abc/def/ghi"
output: a String
where all the substrings separated by "/" are reversed; in this example, the output would be "cba/fed/ihg".
More than the function itself, I care about the general principle behind mutating an iterator generated by the split()
function.
Below is my best effort:
fn reverse_string(input: &mut str) -> String {
input
.to_string()
.split('/')
.map(move |x| x.to_string().rev())
.collect::<String>()
}
The compiler complains that
error[E0599]: no method named `rev` found for type `std::string::String` in the current scope
--> src/main.rs:5:37
|
5 | .map(move |x| x.to_string().rev())
| ^^^
|
= note: the method `rev` exists but the following trait bounds were not satisfied:
`&mut std::string::String : std::iter::Iterator`
`&mut str : std::iter::Iterator`
What does that mean and how can I resolve this problem?
If you are learning iterators, I suggest first deciding what you want to do, before actually doing it.
For example, here is an example with a single memory allocation:
fn reverse_string(input: &str) -> String {
let mut result = String::with_capacity(input.len());
for portion in input.split('/') {
if !result.is_empty() {
result.push('/');
}
for c in portion.chars().rev() {
result.push(c);
}
}
result
}
Iterators generally are focused on pure methods which do not modify their environment. Unfortunately, this can lead to inefficiencies here as this would imply creating and dropping String
left and right.
Now, technically you can mutate the environment in map
(it takes a FnMut
), it's just frowned upon because by convention readers expect it to be pure.
As a result, when you want to bring additional state in, Iterator::fold
is the go-to method:
fn reverse_string(input: &str) -> String {
input
.split('/')
.fold(
String::with_capacity(input.len()),
|mut acc, portion| {
if !acc.is_empty() {
acc.push('/');
}
for c in portion.chars().rev() {
acc.push(c);
}
acc
}
)
}
The first argument is an accumulator, which is passed to each invocation of the closure, which then return it. Finally, at the end of the fold
call, the accumulator is then returned.
This is equivalent to the first function, both in terms of logic and efficiency, but honestly here I kinda prefer the for
version to be honest.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With