Для работы со строками Rust предоставляет тип String, который представляет расширяемые и изменяемые строки
в кодировке UTF-8. В реальности тип String представляет надстройку над вектором Vec<u8>, который хранит набор чисел u8, представляющих символы строки.
Для создания пустого объекта String применяется функция String::new():
let message = String::new();
Для создания объекта String с некоторым начальным содержимым может применяться функция String::from(), в которую передается начальное содержимое строки:
let message = String::from("Hello");
Также можно применять метод to_string(), который есть у разных типов данных и который возвращает объект String:
let message = "hello".to_string();
Для получения длины строки применяется метод chars().count():
fn main(){
let message = String::from("Hello");
let length = message.chars().count();
println!("length: {}", length); // 5
}
Дополнительно функция matches() позволяет найти, сколько раз встречается определенная подстрока в строке:
fn main(){
let sentence = String::from("Hello Rust! Hello METANIT.COM!");
let count = sentence.matches("Hello").count(); // Сколько раз встречается подстрока "Hello"
println!("{}", count); // 2
}
Структура String имеет разные методы для изменения содержимого. Например, метод push() добавляет один символ:
fn main(){
let mut message = String::from("hello");
message.push('?');
println!("Message: {}", message); // Message: hello?
}
Метод push_str() добавляет строку:
fn main(){
let mut message = String::from("hello");
message.push_str(" Rust");
println!("Message: {}", message); // Message: hello Rust
}
Для объединения строк можно использовать оператор сложения +:
fn main(){
let hello = String::from("hello ");
let rust = String::from("Rust");
let message = hello + &rust;
println!("Message: {}", message); // Message: hello Rust
}
Обратите внимание, что для вторая строка передается как ссылка. Так как в реальности оператор + использует метод add(),
который в качестве второго параметра использует строковый слайс:
fn add(self, s: &str) -> String {
Подобным образом можно объединять и большее количество строк - в этом случае все последующие объекты String будут передаваться как ссылки:
fn main(){
let hello = String::from("hello ");
let rust = String::from("Rust");
let metanit = String::from(" on Metanit.com");
let message = hello + &rust + &metanit;
println!("{}", message); // hello Rust on Metanit.com
}
Стоит учитывать, что после объединения строк с помощью их сложения, мы не сможем использовать первую строку, которая передается без ссылки.
fn main(){
let hello = String::from("hello ");
let rust = String::from("Rust");
let message = hello + &rust;
println!("{}", message); // hello Rust
// println!("{}", hello); // !Ошибка владение данными перешло от переменной hello к message
}
Для получения подстроки мы можем указать диапазон символов, который мы хотим получить:
fn main(){
let sentence = String::from("Hello METANIT.COM");
let first_word = &sentence[0..5]; // Берем первые 5 символов
println!("{}", first_word); // Hello
}
Макрос format! позволяет объединить строки в одну общую строку. Он похож на макрос println!, только возвращает
объект String:
fn main(){
let hello = String::from("hello ");
let rust = String::from("Rust");
let message = format!("{} {} on Metanit.com", hello, rust);
println!("{}", message); // hello Rust on Metanit.com
}
В Rust различие между String и &str играет ключевую роль в управлении памятью и семантике владения. Передача &str в различных частях программы довольно эффективна, требует минимальных затрат на выделение памяти и позволяет избежать копирования памяти. Ключевое отличие заключается в принципе владения: >&str — это заимствованный тип, который по существу представляет данные, доступные только для чтения. А String непосредственно владеет данными и представляет возможности чтения и записи. Если углубляться в практические последствия, то String состоит из трех компонентов: указатель на память, в которой хранится содержимое строки, длина и емкость. Примечательно, что выделение памяти для строки происходит в куче, что обеспечивает гибкость в размере. С другой стороны, ссылки на эти данные находятся в стеке. Это различие подчеркивает компромисс между владением и эффективностью, где &str служит облегченной ссылкой, доступной только для чтения, а String предоставляет изменяемое, распределенное в куче хранилище со своим собственным набором атрибутов.