Как да свържа низове?

Как да свържа следните комбинации от типове:

  • 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
Може би това е достатъчно просто, но разбирането му изисква разглеждане на възможните типове сигнатури за Добавяне с низ. - person bluss; 11.05.2015
comment
Благодаря! Можете ли да навлезете малко по-задълбочено в това как &String може да бъде дереферентиран като &str? Коя част от изпълнението му позволява това и/или къде се казва това в doco? - 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-ри параграф да гласи "... случай за String и &String...", тъй като това е редакция с по-малко от 6 знака, ТАКА че няма да ми позволи да направя тази промяна! (или може да е по-фин момент, който не съм разбрал) - 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? Изглежда, че се намира по средата между масив и низ. Търсих в документацията за масив и бързо се обърках. - 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

абв


Трети метод (Using format!()):

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

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

Резултатът от горния код е:

ab

Вижте го и експериментирайте с Rust playground.

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, издаден 2019-10-27: Предлага поддръжка за имплицитни аргументи за извършване на това, което много хора биха познали като интерполация на низове – начин за вграждане на аргументи в низ, за ​​да ги свърже.

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 crate, който предоставя свой собствен вид интерполация на низове:

https://crates.io/crates/ifmt

person JamesHoux    schedule 24.09.2020