1

I'm doing Rust Koans and am stuck on a this question:

#[test]
fn for_loops_two() {
    let words: [&'static str; 3] = ["I", "love", "Rust"];
    let space: &str = " ";
    let mut sentence: String = String::new();
    for word in words.iter() {
        // __
    }

    println!("{:?}", sentence);
    assert!(sentence == "I love Rust".to_string());
}

I know I need to concatenate the string but this will fail:

#[test]
fn for_loops_two() {
    let words: [&'static str; 3] = ["I", "love", "Rust"];
    let mut sentence: String = String::new();
    for word in words.iter() {
        sentence.push_str(word);
    }

    println!("{:?}", sentence); // "ILoveRust"
    assert!(sentence == "I love Rust".to_string());
}

I can add a space after each iteration:

#[test]
fn for_loops_two() {
    let words: [&'static str; 3] = ["I", "love", "Rust"];
    let space: &str = " ";
    let mut sentence: String = String::new();
    for word in words.iter() {
        sentence.push_str(word);
        sentence.push_str(space);
    }

    println!("{:?}", sentence); // "I Love Rust "
    assert!(sentence == "I love Rust".to_string());
}

This will also fail because the final iteration will add a space.

I guess I could write a conditional if we are on the last iteration, but I'm struggling to get the syntax correct. Moreover, I feel like there is a much better solution for all of this and I just can't figure out syntax.

How can I make the assertion above pass with a conditional in the loop to not add the space on the last iteration?

Simson
  • 3,373
  • 2
  • 24
  • 38
Armeen Moon
  • 18,061
  • 35
  • 120
  • 233
  • There is indeed much more simple: https://play.integer32.com/?version=stable&mode=debug&edition=2018&gist=93eeda9e17f3722775ab1a68da8d5058. I let you look at the source of [`join()`](https://docs.rs/itertools/0.8.0/itertools/trait.Itertools.html#method.join) that very basic – Stargateur Mar 24 '19 at 02:15
  • That is much better however it asked me do it in inside a for loop join seems to replace that loop which breaks the initial rule set. Do you see a way to join in the forloop? – Armeen Moon Mar 24 '19 at 02:31
  • 2
    if you can only write in the loop than the only thing I see is to not add the space if your string is empty. But that very ugly. `if sentence.len() != 0 { sentence.push_str(space); } sentence.push_str(word);` And that doesn't produce the desired output if first words are empty. – Stargateur Mar 24 '19 at 02:36
  • 1
    See also [the way to answer this without the silly restrictions](https://stackoverflow.com/q/36941851/155423). – Shepmaster Mar 24 '19 at 11:51

2 Answers2

1

A little late, but here is a solution modifying only the block inside the loop:

#[test]
fn for_loops_two() {
    let words: [&'static str; 3] = ["I", "love", "Rust"];
    let mut sentence: String = String::new();
    for word in words.iter() {
        if sentence != "".to_string() {
            sentence.push(' ')
        }
        sentence.push_str(word)
    }
    println!("{:?}", sentence);
    assert!(sentence == "I love Rust".to_string());
}
milo-ft
  • 21
  • 1
  • 2
0

You can use slice::join:

#[test]
fn for_loops_two() {
    let words: [&'static str; 3] = ["I", "love", "Rust"];

    let sentence = words.join(" ");

    assert!(sentence == "I love Rust".to_string());
}

A note on the linked SliceConcatExt trait: it's listed as unstable on the docs, but the methods are stable - the above compiles just fine under the current stable edition of Rust.

If you'd rather stick to the constraints of the koan and use a for-loop, you can either use an if as you suggest (figuring out whether you're at the end using enumerate), or pop the last space from the end of the string:

#[test]
fn for_loops_two_with_len_check() {
    let words: [&'static str; 3] = ["I", "love", "Rust"];
    const SPACE: char = ' ';
    let number_of_words = words.len();
    let mut sentence = String::new();

    for (i, word) in words.iter().enumerate() {
        sentence.push_str(word);
        if i < number_of_words-1 {
            sentence.push(SPACE);
        }
    }

    assert!(sentence == "I love Rust".to_string());
}


#[test]
fn for_loops_two_with_pop() {
    let words: [&'static str; 3] = ["I", "love", "Rust"];
    const SPACE: char = ' ';
    let mut sentence = String::new();

    for word in words.iter() {
        sentence.push_str(word);
        sentence.push(SPACE);
    }
    let _ = sentence.pop();

    assert!(sentence == "I love Rust".to_string());
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
jelford
  • 2,625
  • 1
  • 19
  • 33
  • 2
    This answer ignores the strict conditions that the OP / koan has placed: you are *only* allowed to edit the code **inside the for loop**. Removing the for loop or adding methods breaks those restrictions. Solutions for the general problem are well-described in [What's an idiomatic way to print an iterator separated by spaces in Rust?](https://stackoverflow.com/q/36941851/155423) – Shepmaster Mar 24 '19 at 12:38
  • 2
    @shepmaster thanks for the clear explanation of why you don't feel this answer is correct. – jelford Mar 24 '19 at 12:46
  • Ahh the pop at the end is clever! I should have guessed this. – Armeen Moon Mar 25 '19 at 16:08