Работа со строками и срезами

RustBeginner
Практиковаться сейчас

Введение

В Rust тип String используется для владеющего, изменяемого текста, а &str — для заимствованного представления текста. Срез строки может представлять всю строку или выбранный диапазон байтов без копирования символов.

Вы передадите владеющий текст функции, которая принимает &str, создадите заимствованное ключевое слово команды и намеренно попросите компилятор проиндексировать текст UTF-8 неподдерживаемым способом, а затем исправите код с помощью среза по корректной границе.

Заимствование текста и строкового литерала как &str

На этом шаге вы передадите два разных источника текста одной функции, которая принимает заимствованный срез строки.

Перейдите в подготовленный проект и откройте его исходный файл:

cd /home/labex/project/string-slices
nano src/main.rs

Тип String владеет изменяемым текстом. Тип &str, который произносится как «срез строки», представляет собой заимствованное представление корректного текста UTF-8. Строковый литерал, например "preview", уже имеет заимствованный тип среза строки, поскольку программа хранит этот текст на протяжении всего запуска.

Функция show_text(label: &str) может работать с обоими видами текста. Замените только /* Step 1: borrow command */ на &command:

    show_text(&command);

Rust автоматически рассматривает заимствованный &String как представление &str, которое требуется функции. При этом символы не копируются и не перемещаются.

Сохраните файл с помощью Ctrl+O, нажмите Enter, а затем выйдите с помощью Ctrl+X. Запустите программу:

cargo run --quiet

Вы должны увидеть следующий результат:

Text: serve reports
Text: preview
Keyword: <>
Status: ✅ ready

Оба вызова передают данные одному и тому же параметру &str. Пустое ключевое слово — это подготовленная заготовка для следующего шага.

Заимствование подстроки без копирования

На этом шаге вы сделаете так, чтобы keyword представлял только префикс serve внутри command.

Откройте исходный файл:

nano src/main.rs

Форма среза &text[start..end] заимствует диапазон. Начальная граница включается, а конечная — нет. Диапазоны строк в Rust отсчитываются в байтах, и каждая ASCII-буква в слове serve занимает один байт. Поэтому диапазон начинается с байта 0 и заканчивается перед байтом 5.

Замените только пустой литерал "" в строке шага 2 на &command[0..5]:

    let keyword = &command[0..5]; // Step 2: replace the empty slice.

Полученный &str указывает внутрь command; для него не выделяется память и не создаётся копия строки. Поскольку command остаётся в области видимости, заимствованное представление остаётся действительным, пока используется keyword.

Сохраните файл и выйдите из редактора, затем запустите программу:

cargo run --quiet

Теперь последняя строка должна выглядеть так:

Keyword: <serve>

Владеющая строка command по-прежнему содержит полный текст serve reports, а keyword — только заимствованное представление первых пяти байтов этой строки.

Учитывайте границы символов UTF-8

На этом шаге вы намеренно попробуете индексировать строку целым числом, изучите объяснение компилятора и замените этот код корректным срезом UTF-8.

Символы UTF-8 занимают разное количество байтов. В строке "✅ ready" символ галочки занимает три байта. Поэтому нулевой байт не обязательно является «первым символом», и Rust не разрешает использовать status[0] для типа String.

Откройте файл и удалите только начальные // из строки вывода шага 3:

nano src/main.rs

Сохраните файл и выйдите из редактора, затем выполните проверку. Ошибка ожидаема:

cargo check

Найдите это неизменяемое сообщение:

error[E0277]: the type `str` cannot be indexed by `{integer}`

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

    let symbol = &status[0..3];
    println!("Status symbol: {symbol}");

Диапазон 0..3 заканчивается сразу после трёх байтов символа. Такой код работает, потому что подготовленный текст и его граница байтов заранее известны; это не универсальный способ найти первый отображаемый пользователю символ в произвольном тексте. Если диапазон проходит через середину символа UTF-8, во время выполнения возникнет паника. Поэтому границы диапазонов строк должны соответствовать известным корректным границам.

Сохраните файл и выйдите из редактора, затем выполните проверку и запустите программу:

cargo check
cargo run --quiet

Полный вывод должен выглядеть так:

Text: serve reports
Text: preview
Keyword: <serve>
Status: ✅ ready
Status symbol: ✅

Успешный вывод подтверждает, что последний срез выбирает один полный символ UTF-8 без копирования исходной строки String.

Итоги

Вы связали владеющие значения String с заимствованными представлениями &str, выбрали подстроку без выделения памяти и узнали, почему текст UTF-8 нельзя рассматривать как массив символов с индексацией по байтам. Безопасные диапазоны строк должны начинаться и заканчиваться на корректных границах UTF-8.