How to mutate iterator elements in Rust to undo substrings

I am trying to make a function that will do the following:
: a String arbitrary length of the form "abc / def / ghi"
output: a String , where all substrings separated by the "/" character are reversed; in this example, the output will be "cba / fed / ihg".

More than the function itself, I care about the general principle of mutating an iterator generated by the split() function.

Below are my best efforts:

 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 this mean and how can I solve this problem?

+5
source share
3 answers

If you are studying iterators, I suggest that you first decide what you want to do before you do it.

For example, here is an example with one 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 usually focus on clean methods that do not change the environment. Unfortunately, this can lead to inefficiencies here, since it involves creating and deleting a String left and right.

Now, technically you can change the environment in map ( FnMut is required), it just frowned because the participating readers expect it to be clean.

As a result, when you want to add additional state, 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 the drive that is passed to each close call, which then returns it. Finally, at the end of the call to fold , the battery is returned.

This is equivalent to the first function, both in terms of logic and efficiency, but honestly, here I prefer the for version to be honest.

+4
source

Here is my attempt:

 fn reverse_string(input: &mut str) -> String { input.split('/') // split the input string by '/' .map(|x| x.chars().rev().collect::<String>()) // reverse the sections and collect them .collect::<Vec<String>>() // collect the reversed sections into a vector of Strings .join("/") // join the strings so they are connected by '/' again } fn main() { let mut s = String::from("abc/def/ghi"); println!("{}", reverse_string(&mut s)); // cba/fed/ihg } 

The main error in the code was that you tried to iterate through String , but it does not implement Iterator ; it must be repeated either through .chars() or .bytes() .

+3
source

Reversing to .chars() may be acceptable sometimes ... but why did Ukraine become Australia?

 println!("{}", reverse_string("abc/dπŸ‡ΊπŸ‡¦f/ghi")); // prints cba/fπŸ‡¦πŸ‡Ίd/ihg 

(Each emoji flag consists of two code points that indicate a country code, and Australia (AU) is the other side of Ukraine (UA). Strange things can also happen with un-emo characters, such as :)

Fortunately, this is easily fixed using the unicode-segmentation box; let's just take the Matthieu solution , replacing chars() with graphemes(true) and push with push_str :

 extern crate unicode_segmentation; use unicode_segmentation::UnicodeSegmentation; 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 g in portion.graphemes(true).rev() { result.push_str(g); } } result } 

Now all our flags look again ( playground ).

+3
source

Source: https://habr.com/ru/post/1274033/


All Articles