Как объединить (join) вектор строк в Rust? Аналог join() из других языков
Основное решение: метод join() у срезов и векторов
В Rust начиная с версии 1.3.0 (стабильная) существует метод join(), который определён для срезов (&[T]) и, соответственно, для векторов (Vec<T>) через автоматическое приведение. Он работает как для строковых срезов (&str), так и для владеющих строк (String).
Вот исправленный код:
let words = vec!["hello".to_string(), "world".to_string(), "rust".to_string()];
let joined = words.join(",");
println!("{}", joined); // "hello,world,rust"
// Если нужны пробелы после запятой:
let joined_with_space = words.join(", ");
println!("{}", joined_with_space); // "hello, world, rust"
Метод join() принимает разделитель как &str и возвращает новую строку String. Он определён в трейте SliceConcatExt<T>, который автоматически доступен для всех срезов.
Почему ваш первый пример не работал?
Скорее всего, вы использовали очень старую версию Rust (до 1.3.0) или пытались вызвать метод на самом векторе без ссылки. В современных версиях Rust vec.join() работает напрямую, так как Vec<T> автоматически разыменовывается в &[T].
Работа с разными типами
Метод join() работает с любыми типами, которые реализуют Borrow<str>:
Vec<String>— владеющие строкиVec<&str>— строковые срезы&[String]или&[&str]— срезы- Даже смешанные типы, если они реализуют трейт (например,
Vec<Cow<str>>)
Пример со смешанными типами:
let mixed: Vec<&dyn AsRef<str>> = vec![&"hello".to_string(), &"world"];
// Но проще просто использовать один тип:
let string_slices = vec!["hello", "world", "rust"];
let joined = string_slices.join(", ");
println!("{}", joined); // "hello, world, rust"
Альтернативный подход с итераторами (для сложной логики)
Если вам нужно больше контроля над процессом (например, условное добавление разделителя или фильтрация), используйте итераторы:
let words = vec!["hello".to_string(), "world".to_string(), "rust".to_string()];
let joined: String = words.iter()
.enumerate()
.map(|(i, word)| {
if i == words.len() - 1 {
word.clone() // последний элемент без запятой
} else {
format!("{}, ", word)
}
})
.collect();
println!("{}", joined); // "hello, world, rust"
Но для простого объединения join() — самый идиоматичный и производительный способ.
Производительность
Метод join() внутри использует String::with_capacity() для предварительного выделения памяти, что делает его эффективным. Для больших векторов это важно.
let large_vec: Vec<String> = (0..1000).map(|i| format!("item_{}", i)).collect();
let result = large_vec.join(","); // быстрее, чем ручной сбор через цикл
<<Использование трейта Itertools из крейта itertools
Если вы работаете с более сложными коллекциями или хотите больше гибкости, можно использовать метод join() из популярного крейта itertools. Он работает с любым итератором, а не только со срезами.
Добавьте в Cargo.toml:
[dependencies]
itertools = "0.12"
Использование:
use itertools::Itertools;
let words = vec!["hello".to_string(), "world".to_string(), "rust".to_string()];
let joined: String = words.iter().join(", ");
println!("{}", joined); // "hello, world, rust"
Преимущества этого подхода:
- Работает с любым итератором, а не только с
Vecили срезом - Позволяет объединять результаты цепочек итераторов (например, после
filter()илиmap()) - Не требует заимствования всего вектора — можно объединять ленивые итераторы
Пример с фильтрацией:
use itertools::Itertools;
let words = vec!["hello", "world", "rust", "alpha", "beta"];
let filtered: String = words.iter()
.filter(|w| w.len() > 4)
.join(", ");
println!("{}", filtered); // "hello, world, alpha"
Однако для простого объединения вектора строк стандартный метод join() предпочтительнее — он не требует внешних зависимостей и работает быстрее благодаря оптимизациям внутри стандартной библиотеки.