Почему у Rust есть String
и str
? Каковы различия между String
и str
? Когда один использует String
вместо str
и наоборот? Один из них становится устаревшим?
String
это динамический тип строки кучи, например Vec
: используйте его, когда вам нужно владеть или изменять ваши строковые данные.
str
является неизменной 1 последовательностью байтов UTF-8 динамической длины где-то в памяти. Поскольку размер неизвестен, его можно обрабатывать только за указателем. Это означает, что str
чаще всего 2 выглядит как &str
: ссылка на некоторые данные UTF-8, обычно называемые «фрагмент строки» или просто «фрагмент». Срез - это просто представление некоторых данных, и эти данные могут быть где угодно, например
"foo"
- это &'static str
. Данные жестко закодированы в исполняемый файл и загружены в память при запуске программы.String
: String
разыменовывает на &str
взгляд из String
данных «s.В стеке : например, следующее создает выделенный стеком байтовый массив, а затем получает представление этих данных в виде&str
:
use std::str;
let x: &[u8] = &[b'a', b'b', b'c'];
let stack_str: &str = str::from_utf8(x).unwrap();
Таким образом, используйте, String
если вам нужны собственные строковые данные (например, передавая строки в другие потоки или создавая их во время выполнения), и используйте, &str
если вам нужен только просмотр строки.
Это идентично взаимосвязи между вектором Vec<T>
и срезом &[T]
и аналогично взаимосвязи между побочным значением T
и побочной ссылкой &T
для общих типов.
1 A str
- фиксированная длина; Вы не можете писать байты за пределами конца или оставлять завершающие недопустимые байты. Поскольку UTF-8 является кодированием с переменной шириной, str
во многих случаях это фактически заставляет все s быть неизменными. Как правило, мутация требует записи большего или меньшего количества байтов, чем было раньше (например, замена a
(1 байт) на ä
(2+ байта) потребует больше места в str
). Существуют специальные методы, которые могут изменять на &str
месте, в основном те, которые обрабатывают только символы ASCII, например make_ascii_uppercase
.
2 Динамически изменяемые типы допускают такие вещи, как Rc<str>
последовательность байтов UTF-8 с подсчетом ссылок начиная с Rust 1.2. Rust 1.21 позволяет легко создавать эти типы.
&str
состоит из двух компонентов: указатель на некоторые байты и длину.»[u8; N]
.Rc<str>
иArc<str>
теперь можно использовать через стандартную библиотеку.У меня есть опыт работы с C ++, и мне было очень полезно думать об этом
String
и&str
в терминах C ++:String
похожа наstd::string
; он владеет памятью и выполняет грязную работу по управлению памятью.&str
похожа наchar*
(но немного более изощренна); он указывает нам на начало фрагмента так же, как вы можете получить указатель на содержимоеstd::string
.Кто-нибудь из них собирается исчезнуть? Я так не думаю. Они служат двум целям:
String
сохраняет буфер и очень практичен в использовании.&str
легкий и должен использоваться для "просмотра" строк. Вы можете искать, разбивать, анализировать и даже заменять фрагменты без необходимости выделять новую память.&str
может выглядеть внутри a,String
поскольку может указывать на какой-либо строковый литерал. Следующий код должен скопировать литеральную строку вString
управляемую память:Следующий код позволяет использовать сам литерал без копирования (только для чтения)
источник
str
используется только как&str
строковый фрагмент, ссылка на байтовый массив UTF-8.String
это то, что раньше было~str
растущим, принадлежащим байтовым массивом UTF-8.источник
~str
теперьBox<str>
~str
был выращиваемым, аBox<str>
не выращиваемым. (Это~str
и~[T]
было магически возможным, в отличие от любого другого~
объекта, именно поэтомуString
иVec<T>
было введено, так что все правила были простыми и последовательными.)Они на самом деле совершенно разные. Во-первых, это
str
не что иное, как вещь уровня типа; это можно рассуждать только на уровне типа, потому что это так называемый тип с динамическим размером (DST). Размер, которыйstr
требуется принять, не может быть известен во время компиляции и зависит от информации времени выполнения - он не может быть сохранен в переменной, потому что компилятору необходимо знать во время компиляции, каков размер каждой переменной.str
Концептуально A представляет собой просто рядu8
байтов с гарантией того, что он образует действительный UTF-8. Насколько большой ряд? Никто не знает до времени выполнения, следовательно, он не может быть сохранен в переменной.Интересно то, что
&str
и любой другой указатель наstr
LikeBox<str>
делает EXIST во время выполнения. Это так называемый «жирный указатель»; это указатель с дополнительной информацией (в данном случае размером с объект, на который он указывает), поэтому он в два раза больше. На самом деле, a&str
довольно близко кString
(но не к&String
). А&str
два слова; один указатель на первый байт astr
и другое число, которое описывает, сколько байтов имеет длинуstr
.Вопреки сказанному, a
str
не обязательно должен быть неизменным. Если вы можете получить&mut str
как эксклюзивный указатель наstr
, вы можете изменить его, и все безопасные функции, которые изменяют его, гарантируют, что ограничение UTF-8 будет поддержано, потому что, если оно нарушается, мы имеем неопределенное поведение, поскольку библиотека предполагает, что это ограничение правда и не проверяет это.Так что же это
String
? Это три слова; два - то же самое, что и для,&str
но он добавляет третье слово, которое является емкостьюstr
буфера в куче, всегда в куче (аstr
не обязательно в куче), которой он управляет до того, как заполнится и должен перераспределить.String
в основном владеетstr
, как они говорят; он контролирует его и может изменить его размер и перераспределить, когда сочтет нужным. Так чтоString
, как сказано, ближе к,&str
чем кstr
.Другое дело, это
Box<str>
; он также владеет a,str
и его представление во время выполнения такое же, как a,&str
но он также владеетstr
непохожим,&str
но не может изменить его размер, потому что не знает своей емкости, поэтому в основном aBox<str>
можно рассматривать как фиксированную длинуString
, размер которой нельзя изменить (вы можете всегда конвертируйте его в a,String
если вы хотите изменить его размер).Очень похожее соотношение существует между
[T]
и заVec<T>
исключением того, что нет UTF-8 , ограничения и он может содержать любой тип, размер которого не является динамическим.Использование
str
на уровне типов в основном для создания общих абстракций с&str
; он существует на уровне типов, чтобы можно было удобно писать черты. Теоретически,str
как тип, вещь не должна существовать и только,&str
но это будет означать, что нужно будет написать много дополнительного кода, который теперь может быть универсальным.&str
супер полезно иметь возможность иметь несколько разных подстрок aString
без необходимости копирования; как сказалString
владеетstr
на куче она управляет , и если вы можете создать только подстрокуString
с новымString
он должен копироваться , потому что все в ржавчине может иметь только один единственный владелец , чтобы иметь дело с безопасностью памяти. Так, например, вы можете нарезать строку:У нас есть две разные подстроки
str
одной и той же строки.string
это тот, который владеет фактическим полнымstr
буфером в куче, а&str
подстроки являются просто жирными указателями на этот буфер в куче.источник
std::String
это просто векторu8
. Вы можете найти его определение в исходном коде . Он выделен кучей и может расти.str
является примитивным типом, также называемым строковым срезом . Срез строки имеет фиксированный размер. Литеральная строка типа likelet test = "hello world"
имеет&'static str
тип.test
является ссылкой на эту статически размещенную строку.&str
нельзя изменить, например,str
имеет изменяемый фрагмент&mut str
, например:pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str)
Но небольшое изменение в UTF-8 может изменить длину его байта, и срез не может перераспределить свой референт.
источник
Проще говоря,
String
тип данных хранится в куче (точно так же какVec
), и у вас есть доступ к этому местоположению.&str
тип среза Это означает, что это просто ссылка на уже присутствующийString
где-то в куче.&str
не делает никакого выделения во время выполнения. Так, по причинам памяти, вы можете использовать&str
болееString
. Но имейте в виду, что при использовании&str
вам, возможно, придется иметь дело с явными временами жизни.источник
str
онview
уже присутствуетString
в куче.Для людей на C # и Java:
String
===StringBuilder
&str
=== (неизменяемая) строкаМне нравится думать о
&str
как о взгляде на строку, как о интернированной строке в Java / C #, где вы не можете изменить ее, только создать новую.источник
Вот быстрое и простое объяснение.
String
- Растущая, доступная структура данных, выделенная кучей. Это может быть приведено к&str
.str
- это (теперь, по мере развития Rust) изменяемая строка фиксированной длины, которая живет в куче или в двоичном файле. Вы можете взаимодействовать сstr
заимствованным типом только через представление среза строки, например&str
.Особенности использования:
Предпочитаете,
String
если хотите владеть или изменять строку - например, передать строку в другой поток и т. Д.Предпочитаю,
&str
если вы хотите, чтобы строка была доступна только для чтения.источник