Добавление к строке внутри цикла for

#rust

#Ржавчина

Вопрос:

Я пытаюсь создать строку, добавив к ней внутри цикла for. По какой-то причине строка перемещается в цикл for, и я не могу заставить ее работать. Я, очевидно, что-то упускаю. Вот короткий фрагмент кода, который демонстрирует это поведение:

 fn main() {
  let s = format!("some string");
  for x in vec!(1,2).move_iter() {
    s.append("some other string");
  }
}
  

Я получаю следующую ошибку от компилятора ( 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
  

Ответ №1:

Обновление: в последней версии Rust (начиная с 1.0.0-alpha) append() метод больше не существует. Однако оба String Vec метода and перегружены, и он ведет себя точно так же, как старый append() :

 let s = String::new()   "some part"   "next part";
  

Операторы всегда принимают свои операнды по значению, поэтому это не вызывает ненужных перераспределений. Правильный операнд должен быть amp;str for String или amp;[T] for Vec<T> ; в последнем случае T должно быть Clone .

В любом случае, push_str() все еще доступно.


Причина, по которой вы получаете эту ошибку, заключается в том, что вы используете неправильный метод 🙂

String::append() метод похож на конструктор; предполагается, что он используется следующим образом:

 let s = String::new()
    .append("some part")
    .append("next part");
  

Фактически, вы сможете использовать его в своем коде:

 let mut s = "some string".to_string();
for amp;x in [1, 2].iter() {
    s = s.append("some other string");  // note the reassignment
}
  

Это происходит потому append() , что имеет эту подпись:

 fn append(self, other: amp;str) -> String { ... }
  

То есть он принимает получателя по значению и перемещается в вызов. Это позволяет легко создавать цепочки, но несколько неудобно, если вам нужно изменить существующую переменную.

Вызывается метод, который вы ищете push_str() :

 let mut s = "some string".to_string();
for amp;x in [1, 2].iter() {
    s.push_str("some other string");
}
  

Он просто добавляет переданный фрагмент строки к существующему String . Обратите внимание, что вы также должны пометить s как изменяемый. Вам также не нужно выделять новый вектор с помощью vec!() , здесь достаточно статического массива.

Тем не менее, лучше полностью избегать мутации, если это возможно. предложение @A.B. использовать fold() абсолютно правильно.

Комментарии:

1. Спасибо. Пока я использую push_str и посмотрю, смогу ли я преобразовать код в более функциональный стиль, чтобы избежать использования mut позже.

Ответ №2:

Сигнатура функции append

 fn append(self, second: amp;str) -> String
  

self передается по значению, что означает, что если бы self реализовал свойство копирования, оно было бы скопировано, в противном случае оно было бы перемещено. Строка не реализует копирование, поэтому она будет перемещена.

Операция складывания может удерживать строку между добавлениями:

 let s = format!("some string");
let s = vec!(1,2).iter()
    .fold(s, |s, num| s.append("some other string"));
println!("{}",s);
  

Я предполагаю, что в конечном итоге вы хотите прочитать строки из вектора. Если вы просто пытаетесь повторить операцию несколько раз, вам следует использовать range(0, n) or std::iter::Repeat::new("some other string").take(n) в качестве итератора для выполнения сворачивания.

Комментарии:

1. Я посмотрю, смогу ли я адаптировать код для использования fold, поскольку это выглядит аккуратнее, чем обычный цикл for .