Reputation: 3401
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?
Upvotes: 0
Views: 446
Reputation: 7927
Two points:
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
.
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