1

With a list input of &str, I'm trying to create a String which contains a proverb based on the inputs.

Instead of String::from I also tried .to_string() but that doesn't seem to help matters.

pub fn build_proverb(list: &[&str]) -> String {
    let mut string = String::from(format!("For want of a {} the {} was lost.\n", list[0], list[1]));

    if list.len() > 2 {
        (3..list.len() + 1).map(|x| string = string + String::from(format!("For want of a {} the {} was lost.\n", list[x], list[x-1])));
    }

    string = string + &(format!("And all for the want of a {}.", list[0])).to_string();

    return string.to_string();
}

The error is:

error: mismatched types expected &str, found struct 'std::string::String'.

This is on the String::from(format!("For want of a {} the {} was lost.\n", list[x], list[x-1])) line.

What's confusing to me is that I'm adding a String to a String - why is it expecting a &str?

pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
Caranthir
  • 13
  • 2
  • Possible duplicate of [How do I concatenate strings?](https://stackoverflow.com/questions/30154541/how-do-i-concatenate-strings) – trent Aug 18 '19 at 00:01
  • Also [Why is it not possible to concatenate two Strings in Rust without taking a reference to one of them?](https://stackoverflow.com/questions/39917173/why-is-it-not-possible-to-concatenate-two-strings-in-rust-without-deref-coercing) – trent Aug 18 '19 at 00:03
  • [How can I append a formatted string to an existing String?](https://stackoverflow.com/questions/28333612/how-can-i-append-a-formatted-string-to-an-existing-string) is probably the question you didn't know you needed an answer to. – trent Aug 18 '19 at 00:14

1 Answers1

2

format! already returns a String, so there's no need for String::from(format!(...)), and it's also an error because it expects a &str, not a String returned by format!.

You'll also get an error in the lambda:

string = string + String::from(format!(...))

...even if you remove String::from, because it's not possible to add two Strings like that, but it is possible to add a String and a &str, so I think you should borrow like this:

string = string + &format!(...)

The same goes for this line:

string = string + &(format!("And all for the want of a {}.", list[0])).to_string();

Moreover, your usage of map won't actually execute the lambda for each element of the range. It'll just create a Map iterator, over which you'll have to iterate with a loop to actually make it execute the lambda, so you could as well iterate over the range itself and modify your string in the loop.

I'm also not terribly sure about why you're returning string.to_string() when you could've returned string itself.


I also think you have an off-by-one error in your range, so after fixing that, I ended up with this:

fn do_it(list: Vec<&str>) -> String {
    let mut string = format!("For want of a {} the {} was lost.\n", list[0], list[1]);

    // BTW, you don't need this `if` statement because empty ranges, like `2..2`, are perfectly fine
    if list.len() > 2 {
        // These ranges do not include `list.len()`, so your program won't panic, because this index doesn't exist
        for x in 2 .. list.len() {
            string += &format!("For want of a {} the {} was lost.\n", list[x], list[x-1])
        }
    }

    string + &format!("And all for the want of a {}.", list[0])  // Return the result of concatenation
}

fn main() {
    let list = vec!["something", "test", "StackOverflow"];
    let result = do_it(list);

    println!("The result is: {}", result)
}

Output (it works, but your post doesn't say what it should output, so I can't say if it's the correct result):

The result is: For want of a something the test was lost.
For want of a StackOverflow the test was lost.
And all for the want of a something.
ForceBru
  • 43,482
  • 10
  • 63
  • 98
  • Thank you - a lot of useful information here - the map bit especially was useful! – Caranthir Aug 17 '19 at 22:18
  • @Caranthir, glad it helped! Please keep in mind that I'm a beginner too, so the the wording and the code may be clumsy. Feel free to ask if something's unclear and check the latest version of my answer for an example program that seems to work – ForceBru Aug 17 '19 at 22:29
  • What you should do for this *particular* case is `write!(&mut string, ...)` which will skip the allocation if `string` has the capacity for it. If you were doing something other than appending to `string`, you would probably want to write something like `string = format!("{}...", string, ...);` – trent Aug 18 '19 at 00:08