ANimator120
ANimator120

Reputation: 3401

Split string by char, then rejoin with new char insertions in Rust

I'd like to split a string by a char, then for each of those split chunks, append a new string. Then I'd like to rejoin these chunks into a single string.

fn main(){
 let my_string_start = String::From("Hello, Goodmorning");
 let augmented_string = my_string_start split(",").flat_map(|f| format!("{} and",f)).collect();
 assert_eq!(augmented_string, String::from("Hello, and Goodmorning"));
}

But this gives error:

error[E0277]: `String` is not an iterator
 --> src/main.rs:3:53
  |
3 |  let augmented_string = &my_string_start.split(",").flat_map(|f| format!("{} and",f)).collect();
  |                                                     ^^^^^^^^ `String` is not an iterator; try calling `.chars()` or `.bytes()`
  |
  = help: the trait `Iterator` is not implemented for `String`
  = note: required because of the requirements on the impl of `IntoIterator` for `String`

error[E0599]: the method `collect` exists for struct `FlatMap<std::str::Split<'_, &str>, String, [closure@src/main.rs:3:62: 3:85]>`, but its trait bounds were not satisfied
   --> src/main.rs:3:87
    |
3   |    let augmented_string = &my_string_start.split(",").flat_map(|f| format!("{} and",f)).collect();
    |                                                                                         ^^^^^^^ method cannot be called on `FlatMap<std::str::Split<'_, &str>, String, [closure@src/main.rs:3:62: 3:85]>` due to unsatisfied trait bounds
    |
    = note: the following trait bounds were not satisfied:
            `String: IntoIterator`
            which is required by `FlatMap<std::str::Split<'_, &str>, String, [closure@src/main.rs:3:62: 3:85]>: Iterator`
            `FlatMap<std::str::Split<'_, &str>, String, [closure@src/main.rs:3:62: 3:85]>: Iterator`
            which is required by `&mut FlatMap<std::str::Split<'_, &str>, String, [closure@src/main.rs:3:62: 3:85]>: Iterator`

What's a valid way to do this?

Playground

Upvotes: 0

Views: 446

Answers (1)

Alexey S. Larionov
Alexey S. Larionov

Reputation: 7927

Two points:

  1. You forgot what flat_map actually does.

It runs over an iterator and for each element it's expected to output a separate Iterator, those iterators are chained together into a single Iterator.

In your case you wanted to first split the string (every elements of split iterator is some &str), you then need to simply apply map (because we want to take a single element - part of initial string, and output a single element - its extended version)

There's also one unpleasant obstacle - we'll also extend the last part with ", and", which is not quite what you want.

Finally we collect the iterator into a new String.

  1. You worked with my_string_start by reference.

&my_string_start is of type &str and you can't collect it later. But split method automatically takes the String by reference, so no worries about accidentally moving your string out. You can safely remove this &, and the whole thing will be collected in the end into another String

fn main(){
 let my_string_start = String::from("Hello, Goodmorning");
 let augmented_string: String = my_string_start
    .split(",")
    .map(|f| format!("{}, and ", f))
    .collect();
 assert_eq!(augmented_string, String::from("Hello, and Goodmorning"));
}

EDIT

I tried to solve the issue of the trailing , and after the last element, and solving it with current capabilities of Rust is quite ugly and dirty. There's one unstable feature of Rust, that is available in Nightly version of the compiler, and it's just so good for your exact task, take a look.

intersperse takes an iterator, and inserts some element in between every element (not before and not after the iterator, only inside the iterator). With it you simply get what you wanted

#![feature(iter_intersperse)]

fn main(){
 let my_string_start = String::from("Hello, Goodmorning");
 let augmented_string: String = my_string_start
    .split(",")
    .intersperse(", and")
    .collect();
 assert_eq!(augmented_string, String::from("Hello, and Goodmorning"));
}

Upvotes: 2

Related Questions