Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Appending to a string inside a for loop

Tags:

rust

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
like image 771
ujh Avatar asked Jun 13 '14 07:06

ujh


People also ask

How do I concatenate a string in a for loop?

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.

Can you append in a for loop Python?

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.

How do I append to an existing string?

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.


2 Answers

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.

like image 109
Vladimir Matveev Avatar answered Oct 06 '22 13:10

Vladimir Matveev


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.

like image 28
A.B. Avatar answered Oct 06 '22 13:10

A.B.