I started programming with Rust this week and I am having a lot of problems understanding how Strings work.
Right now, I am trying to do a simple program that prints a list of players appending their order(for learning purposes only).
let res : String = pl.name.chars().enumerate().fold(String::new(),|res,(i,ch)| -> String {
res+=format!("{} {}\n",i.to_string(),ch.to_string());
});
println!("{}", res);
This is my idea, I know I could just use a for loop but the objective is to understand the different Iterator functions.
So, my problem is that the String concatenation does not work.
Compiling prueba2 v0.1.0 (file:///home/pancho111203/projects/prueba2)
src/main.rs:27:13: 27:16 error: binary assignment operation `+=` cannot be applied to types `collections::string::String` and `collections::string::String` [E0368]
src/main.rs:27 res+=format!("{} {}\n",i.to_string(),ch.to_string());
^~~
error: aborting due to previous error
Could not compile `prueba2`.
I tried using &str but it is not possible to create them from i
and ch
values.
First, in Rust
x += y
is not overloadable, so+=
operator won't work for anything except basic numeric types. However, even if it worked for strings, it would be equivalent tox = x + y
, like in the following:Even if this were allowed by the type system (it is not because
String + String
"overload" is not defined in Rust), this is still not howfold()
operates. You want this:or, as a compilable example,
When you perform a fold, you don't reassign the accumulator variable, you need to return the new value for it to be used on the next iteration, and this is exactly what
res + format!(...)
do.Note that I've removed
to_string()
invocations because they are completely unnecessary - in fact,x.to_string()
is equivalent toformat!("{}", x)
, so you only perform unnecessary allocations here.Additionally, I'm taking
format!()
result by reference:&format!(...)
. This is necessary because+
"overload" for strings is defined forString + &str
pair of types, so you need to convert fromString
(the result offormat!()
) to&str
, and this can be done simply by using&
here (because of deref coercion).In fact, the following would be more efficient:
which could be written more idiomatically as
(try it on playpen)
This way no extra allocations (i.e. new strings from
format!()
) are created. We just fill the string with the new data, very similar, for example, to howStringBuilder
in Java works.use std::fmt::Write
here is needed to allow callingwrite!()
on&mut String
.I would also suggest reading the chapter on strings in the official Rust book (and the book as a whole if you're new to Rust). It explains what
String
and&str
are, how they are different and how to work with them efficiently.