Как объединить строки?

Как объединить следующие комбинации типов:

  • str и str
  • String и str
  • String и String

person jsalter    schedule 10.05.2015    source источник
comment
Обратите внимание, что str и &str являются разными типами, и в 99% случаев вам следует заботиться только о &str. Есть и другие вопросы, подробно описывающие различия между ними.   -  person Shepmaster    schedule 10.05.2015
comment
Отвечает ли это на ваш вопрос? Как объединить статические строки в Rust   -  person Nathan McMillan    schedule 06.04.2021


Ответы (5)


Когда вы объединяете строки, вам необходимо выделить память для хранения результата. Начать проще всего с String и &str:

fn main() {
    let mut owned_string: String = "hello ".to_owned();
    let borrowed_string: &str = "world";
    
    owned_string.push_str(borrowed_string);
    println!("{}", owned_string);
}

Здесь у нас есть собственная строка, которую мы можем видоизменить. Это эффективно, поскольку потенциально позволяет нам повторно использовать выделение памяти. Аналогичный случай для String и String, поскольку &String может быть разыменовано как &str.

fn main() {
    let mut owned_string: String = "hello ".to_owned();
    let another_owned_string: String = "world".to_owned();
    
    owned_string.push_str(&another_owned_string);
    println!("{}", owned_string);
}

После этого another_owned_string остается нетронутым (обратите внимание на отсутствие квалификатора mut). Есть еще один вариант, который потребляет String, но не требует его изменения. Это реализация Add трейта, который принимает String как левую часть и &str как правую часть:

fn main() {
    let owned_string: String = "hello ".to_owned();
    let borrowed_string: &str = "world";
    
    let new_owned_string = owned_string + borrowed_string;
    println!("{}", new_owned_string);
}

Обратите внимание, что owned_string больше не доступен после вызова +.

Что, если бы мы хотели создать новую строку, не трогая обе? Самый простой способ - использовать format!:

fn main() {
    let borrowed_string: &str = "hello ";
    let another_borrowed_string: &str = "world";
    
    let together = format!("{}{}", borrowed_string, another_borrowed_string);

    // After https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html
    // let together = format!("{borrowed_string}{another_borrowed_string}");

    println!("{}", together);
}

Обратите внимание, что обе входные переменные неизменяемы, поэтому мы знаем, что они не затронуты. Если мы хотим сделать то же самое для любой комбинации String, мы можем использовать тот факт, что String также можно отформатировать:

fn main() {
    let owned_string: String = "hello ".to_owned();
    let another_owned_string: String = "world".to_owned();
    
    let together = format!("{}{}", owned_string, another_owned_string);

    // After https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html
    // let together = format!("{owned_string}{another_owned_string}");
    println!("{}", together);
}

Однако вы не должны использовать format!. Вы можете клонировать одну строку и добавить другую. строка в новую строку:

fn main() {
    let owned_string: String = "hello ".to_owned();
    let borrowed_string: &str = "world";
    
    let together = owned_string.clone() + borrowed_string;
    println!("{}", together);
}

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

person Shepmaster    schedule 10.05.2015
comment
Что вы думаете о символе Add / +? Вы можете прикрыть это, если хотите. - person bluss; 11.05.2015
comment
Может быть, это достаточно просто, но для понимания этого нужно посмотреть возможные сигнатуры типов для Add with String. - person bluss; 11.05.2015
comment
Спасибо! Можете ли вы немного углубиться в то, как & String можно разыменовать как & str? Какая часть его реализации допускает это и / или где это сказано в документе? - person jsalter; 12.05.2015
comment
@jsalter - это отдельная тема, так что это может быть еще один вопрос верхнего уровня. Я обновил ссылку на соответствующие документы (как минимум, насколько я могу ...) - person Shepmaster; 12.05.2015
comment
@ChrisMorgan Следует отметить, что несоответствие .to_owned() и .to_string() было исправлено с момента вышеприведенного комментария благодаря специализации impl. Они оба теперь имеют одинаковую производительность при вызове &str. Соответствующая фиксация: github.com/rust-lang/rust/pull/32586/files - person chad; 23.11.2016
comment
Спецификации типов определенно полезны в этом коде. - person Zelphir Kaltstahl; 31.10.2017
comment
Если 2-е предложение 2-й параграф будет читать «... case for String and _2 _...», поскольку это редактирование менее 6 символов, SO не позволит мне внести это изменение! (или может быть более тонкий момент, который я не понял) - person paddyg; 17.11.2017
comment
@paddyg Да, это тонко. Оба начальных типа String, но затем вы берете ссылку на один (&String), который может быть приведен к &str. Я поставил весь путь String - ›&String -› &str, потому что новички могут даже не догадываться, что можно взять ссылку на String. :-) - person Shepmaster; 17.11.2017

Чтобы объединить несколько строк в одну строку, разделенную другим символом, есть несколько способов.

Самое приятное, что я видел, - это использование метода join в массиве:

fn main() {
    let a = "Hello";
    let b = "world";
    let result = [a, b].join("\n");

    print!("{}", result);
}

В зависимости от вашего варианта использования вы также можете предпочесть больший контроль:

fn main() {
    let a = "Hello";
    let b = "world";
    let result = format!("{}\n{}", a, b);

    print!("{}", result);
}

Я видел еще несколько ручных способов, некоторые из которых позволяют избежать одного или двух распределений здесь и там. Для удобства чтения я считаю, что двух вышеупомянутых достаточно.

person Simon Whitehead    schedule 17.01.2017
comment
Где join задокументировано? Кажется, что он находится на полпути между массивом и строкой. Я просмотрел документацию по array и быстро запутался. - person Duane J; 11.01.2018
comment
@DuaneJ join фактически привязан к признаку SliceContactExt. Признак отмечен как нестабильный, но его методы стабильны и включены в Prelude поэтому по умолчанию их можно использовать везде. Команда, похоже, хорошо понимает, что эта черта не обязательно должна существовать, и я думаю, что в будущем с ней все изменится. - person Simon Whitehead; 11.01.2018
comment
Возможно, вам стоит упомянуть, что join более эффективен, чем s1.to_owned().push_str(s2) для объединения двух str, поскольку он позволяет избежать второго распределения. - person Ibraheem Ahmed; 19.03.2021

Простые способы объединения строк в Rust

В Rust доступны различные методы объединения строк.

Первый способ (с использованием concat!()):

fn main() {
    println!("{}", concat!("a", "b"))
}

Вывод приведенного выше кода:

ab


Второй способ (с использованием оператора push_str() и +):

fn main() {
    let mut _a = "a".to_string();
    let _b = "b".to_string();
    let _c = "c".to_string();

    _a.push_str(&_b);

    println!("{}", _a);

    println!("{}", _a + &_c);
}

Вывод приведенного выше кода:

ab

abc


Третий способ (Using format!()):

fn main() {
    let mut _a = "a".to_string();
    let _b = "b".to_string();
    let _c = format!("{}{}", _a, _b);

    println!("{}", _c);
}

Вывод приведенного выше кода:

ab

Проверьте это и поэкспериментируйте с игровой площадкой Rust.

person ASHWIN RAJEEV    schedule 14.01.2020
comment
Этот ответ не добавляет ничего нового к существующим ответам. - person Shepmaster; 14.01.2020

Я думаю, что метод concat и здесь также следует упомянуть +:

assert_eq!(
  ("My".to_owned() + " " + "string"),
  ["My", " ", "string"].concat()
);

и есть также макрос concat!, но только для литералов :

let s = concat!("test", 10, 'b', true);
assert_eq!(s, "test10btrue");
person suside    schedule 08.04.2019
comment
+ уже упоминается в существующем ответе. (Это реализация признака Add, которая принимает String как левую часть и &str как правую:) - person Shepmaster; 14.01.2020
comment
Правда, существующий ответ настолько широк, что я не заметил. - person suside; 15.01.2020
comment
Лучший ответ на данный момент. Просто используйте метод массива или concat для строк. Макросы просто удобны для того, чтобы скрыть некоторый синтаксис, а не изобретать сложный синтаксис, делающий основной язык загадочным. Добавление трейта может быть приятным для объектов, но, по крайней мере, может сбивать с толку. - person Anssi; 31.07.2020

Конкатенация строковой интерполяцией

RFC 2795, выпущенный 27.10.2019: Предлагает поддержку неявных аргументов для выполнения того, что многие люди знают как интерполяцию строк - способ встраивания аргументов в строку для их объединения.

RFC: https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html.

Статус последней проблемы можно найти здесь: https://github.com/rust-lang/rust/issues/67984

На момент написания этой статьи (2020-9-24) я считаю, что эта функция должна быть доступна в сборке Rust Nightly.

Это позволит вам выполнить конкатенацию с помощью следующего сокращения:

format_args!("hello {person}")

Это эквивалентно этому:

format_args!("hello {person}", person=person)

Также есть ящик ifmt, который предоставляет свой собственный тип интерполяции строк:

https://crates.io/crates/ifmt

person JamesHoux    schedule 24.09.2020