Найти в Дзене
Ржавый код

Заметки о Rust - строка и срез строки

Оглавление

Эта статья в блоге знакомит с понятиями типа `String` и заимствованного аналога `&str`, то есть срез строк, выделяя их различия. Далее приводится несколько внутренних деталей реализации.

String

`String` - это строка в кодировке UTF-8. Термин «владелец» означает, что переменная `s` типа `String` имеет право собственности на содержимое строки. Когда `s` выходит из области действия, строка владельца удаляется.

-2

В стандартной библиотеке строка определяется следующим образом:

-3

Да, именно, строка - это просто вектор байтов (`u8`). Однако стандартная библиотечная реализация `String` гарантирует, что массив байтов, указанный `vec`, фактически содержит только действующие байты UTF-8.

&str

`str` - «строковый срез», последовательность байтов в памяти, которая, как известно, является допустимым текстом UTF-8. Это примитивный тип в Rust и большую часть времени используется в заимствованной форме `&str`.

Причина этого в том, что `str` не является размерным типом `Sized`, означает, что его размер не известен статически (во время компиляции), а компилятор Rust должен знать размер всех локальных переменных, чтобы гарантировать безопасность, предоставляемые языком Rust.

Фактически, если вы по пытаетесь создать собственную версию `str`:

-4

при построении предоставленного фрагмента кода (cargo build) компилятор возвращает следующую ошибку (протестирована на MacOS, Rust v1.70.0):

-5

То есть `str` не реализует признак `Size` и поэтому его размер не известен статически во время компиляции.

Заимствованная форма `&str` вместо этого является просто ссылкой, и ее размер всегда известен статически во время компиляции. Более точно `&str` является «толстым указателем», т.е. ссылкой на тип динамического размера (DST) (`str`), который содержит как указатель на данные, так и некоторую информацию, которая делает DST «полным» (т.е. длину `str`).

Это можно увидеть на простом примере. Рассмотрим следующий код:

-6

и скомпилировать его в режиме отладки (cargo build).

Теперь мы можем запустить получившийся исполняемый файл в отладчике и разместить точку останова непосредственно перед вызовом функции `foo` (на снимке экрана ниже показан отладчик Ghidra, работающий на ноутбуке Apple MacBook pro с процессором ARM).

-7

Строковый срез (`&str`) `ss` создается командой по адресу `0x1000056cc (bl < >::index)` и помещается в стек (как и ожидалось, учитывая, что `ss` является локальной переменной). Как мы уже говорили выше, `ss` является жирным указателем и содержит как указатель на строковые данные, так и информацию о длине. Например, это можно наблюдать в консоли отладчика в левом нижнем углу изображения:

  • Указатель стека, при считывании регистра `sp`, указывает на `0x000000016bddad60`;
  • Информация о длине среза, распечатанная с помощью `x/1gx $sp+0x10`, равна `0x5`;
  • Указатель на строковые данные, напечатанные с помощью `x/1gx $sp+0x8`, указывает на `0x00006000004ac010`;
  • Мы можем распечатать значение, указанное этим адресом, в памяти `-s1 -fc -c 9 0x00006000004ac010` и убедиться, что этот адрес на самом деле является расположением ожидаемой строки.

&String

Обратите внимание, что если `s` является переменной типа `String`, `&s` является только ссылкой на `String` (т. е. `&String`). Концептуально это то же самое, что происходит, когда у вас есть переменная `v` типа `Vec<T>` и вы берете ссылку `&v`. В итоге появляется ссылка на `Vec<T>`.

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

-8

соответствующий выход на консоль:

-9

Обратите внимание на `&` перед второй строкой вывода.

Ссылка на строку (`&String`) отличается от ссылки `&str`. Однако `&String` часто может автоматически принудительно преобразовываться в `&str`.

Примеры

-10

Ссылки

Статья на rusty-code.ru