-2

I am trying to collect vector of string to string with separator $.

let v = [String::from("bump"), String::from("sage"),String::from("lol"), String::from("   kek   ")]; 
let s: String = v.into_iter().map(|x| x.push_str("$")).collect();
println!("{:?}",s );

The code above does not work, but this:

let v = [String::from("hello"), String::from("world"),String::from("shit"), String::from("   +15   ")]; 
let s: String = v.into_iter().collect();
println!("{:?}",s );

is working. How do I solve this problem?

Peterrabbit
  • 2,166
  • 1
  • 3
  • 19

3 Answers3

3

Your code isn't working because push_str() does not return the string.

So your map() function maps from String to (), because you don't return x from it.

Further, x is not mutable, so you cannot call push_str() on it. You have to declare it mut.

This is your code, minimally modified so that it works:

fn main(){
    let v = [String::from("bump"), String::from("sage"),String::from("lol"), String::from("   kek   ")]; 
    let s: String = v.into_iter().map(|mut x| {x.push_str("$"); x}).collect();
    println!("{:?}",s );
}
"bump$sage$lol$   kek   $"

Further, if you only push a single character, do push('$') instead.

You will notice, however, that there is a $ at the end of the string. Your usecase is perfect for reduce(), so I'd use @Aleksander's answer instead.

Finomnis
  • 18,094
  • 1
  • 20
  • 27
1

You must return strings from your map.

Note that push_str doesn't return anything.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=50534d454a46093299ce38682733c86a

fn main() {
    let v = [
        String::from("bump"),
        String::from("sage"),
        String::from("lol"),
        String::from(" kek "),
    ];
    let s: String = v
        .iter()
        .map(|x| {
            let mut x = x.to_owned();
            x.push_str("$");
            x
        })
        .collect();
    println!("{}", s);
}

EDIT

If your real use case is more complex and you must use a iterator and a map, you should prefer above answers which are better done (no need to own the returned strings from the map because you collect them into a new string anyway).

But that said if the only purpose is to join your Vec with a separator you should simply do

fn main() {
    let v = [
        String::from("bump"),
        String::from("sage"),
        String::from("lol"),
        String::from(" kek "),
    ];
    
    println!("{}", v.join("$"));
}
Peterrabbit
  • 2,166
  • 1
  • 3
  • 19
1

You can use Iterator::reduce. Note that it will put separators only between items (and not at the end of string like in Petterrabit's answer) and it will re-use the allocation of fist string (which results in slightly better memory efficiency).

fn main() {
    let v = [
        String::from("bump"),
        String::from("sage"),
        String::from("lol"),
        String::from(" kek "),
    ];
    let s: String = v
        .into_iter()
        .reduce(|mut acc, x| {
            acc.push('$');
            acc.push_str(&x);
            acc
        })
        .unwrap_or_default();
    println!("{}", s);
}
Aleksander Krauze
  • 3,115
  • 7
  • 18