I'm trying to build a string by appending to it inside a for loop. For some reason the string gets moved into the for loop and I can't get it to work. I'm obviously missing something. Here's a short code snippet that exhibits this behaviour:
fn main() {
let s = format!("some string");
for x in vec!(1,2).move_iter() {
s.append("some other string");
}
}
I get the following error from the compiler (rustc 0.11.0-pre (c0a6f72 2014-06-12 14:17:13 +0000)
):
test.rs:4:9: 4:10 error: use of moved value: `s`
test.rs:4 s.append("some other string");
^
test.rs:4:9: 4:10 note: `s` moved here because it has type `collections::string::String`, which is non-copyable (perhaps you meant to use clone()?)
test.rs:4 s.append("some other string");
^
error: aborting due to previous error
This rule finds code that performs string concatenation in a loop using the + operator. If the enclosing loop in question is executed many times, the use of the + operator may be inefficient.
Python provides a method called . append() that you can use to add items to the end of a given list. This method is widely used either to add a single item to the end of a list or to populate a list using a for loop.
You can use the '+' operator to append two strings to create a new string. There are various ways such as using join, format, string IO, and appending the strings with space.
Update: In the latest Rust (as of 1.0.0-alpha) append()
method does no longer exist. However, both String
and Vec
have +
method overloaded, and it behaves exactly like the old append()
:
let s = String::new() + "some part" + "next part";
Operators always take their operands by value, so this does not cause unnecessary reallocations. The right operand must be &str
for String
or &[T]
for Vec<T>
; in the latter case, T
must be Clone
.
Anyway, push_str()
is still available.
The reason you're getting this error is that you're using the wrong method :)
String::append()
method is builder-like; it is supposed to be used like this:
let s = String::new()
.append("some part")
.append("next part");
In fact, you will be able to make use of it in your code:
let mut s = "some string".to_string();
for &x in [1, 2].iter() {
s = s.append("some other string"); // note the reassignment
}
This happens because append()
has this signature:
fn append(self, other: &str) -> String { ... }
That is, it takes the receiver by value, and it is moved into the call. It allows easy chaining, but is somewhat inconvenient if you need to modify existing variable.
The method you're looking for is called push_str()
:
let mut s = "some string".to_string();
for &x in [1, 2].iter() {
s.push_str("some other string");
}
It just appends passed string slice to the existing String
. Note that you also have to mark s
as mutable. You also don't need to allocate new vector using vec!()
, static array will suffice here.
That said, it is better to avoid mutation completely if possible. @A.B.'s suggestion to use fold()
is absolutely right.
The function signature of append
is
fn append(self, second: &str) -> String
self
is passed by-value, which means that if self implemented the Copy trait, it would be copied, otherwise it would be moved. String doesn't implement Copy, therefore it will be moved.
A fold operation can hold the string between the appends:
let s = format!("some string");
let s = vec!(1,2).iter()
.fold(s, |s, num| s.append("some other string"));
println!("{}",s);
I'm assuming that ultimately you want to read strings from the vector. If you're just trying to repeat an operation several times, you should use range(0, n)
or std::iter::Repeat::new("some other string").take(n)
as iterator to do the folding on.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With