Введение
В 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.


