Почему у Rust есть String
и str
? Каковы различия между String
и str
? Когда можно использовать String
вместо str
и наоборот? Один из них становится устаревшим?
Каковы различия между Rust `String` и` str`?
Ответ 1
String
- это динамический тип строки кучи, такой как Vec
: используйте его, когда вам нужно владеть или изменять свои строковые данные.
str
- это неизменяемая 1 последовательность байтов UTF-8 динамической длины где-то в памяти. Поскольку размер неизвестен, его можно обрабатывать только за указателем. Это означает, что str
чаще всего 2 отображается как &str
: ссылка на некоторые данные UTF-8, обычно называемые "фрагмент строки" или просто "фрагмент". Срез - это просто представление некоторых данных, и эти данные могут быть где угодно, например
- В статическом хранилище: строковый литерал
"foo"
является&'static str
. Данные жестко закодированы в исполняемый файл и загружены в память при запуске программы. - Внутри кучи размещены
String
:String
на представление&str
данныхString
. -
В стеке: например, следующее создает массив байтов, выделенный в стеке, а затем получает представление этих данных как
&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.str
является фиксированной длиной; Вы не можете писать байты за пределами конца или оставлять завершающие недопустимые байты. Поскольку UTF-8 является кодированием с переменной шириной, во многих случаях это фактически заставляет все str
быть неизменяемыми. В целом, для мутации требуется записать больше или меньше байтов, чем было раньше (например, замена a
(1 байт) на ä
(2+ байт) потребует больше места в str
). Существуют специальные методы, которые могут модифицировать &str
в основном те, которые обрабатывают только символы ASCII, например make_ascii_uppercase
.
2Динамически изменяемые типы допускают такие вещи, как Rc<str>
для последовательности ссылок UTF-8, подсчитываемой ссылками начиная с Rust 1.2. Rust 1.21 позволяет легко создавать эти типы.
Ответ 2
У меня есть фон С++, и мне было очень полезно думать о String
и &str
в терминах С++:
- Rust
String
похож наstd::string
; он владеет памятью и выполняет грязную работу по управлению памятью. - Rust
&str
похож наchar*
(но немного сложнее); он указывает нам на начало фрагмента так же, как вы можете получить указатель на содержимоеstd::string
.
Или они исчезнут? Я так не думаю. Они служат двум целям:
String
хранит буфер и очень практичен в использовании. &str
является легким и должен использоваться для "поиска" в строках. Вы можете искать, разделять, анализировать и даже заменять куски, не выделяя новую память.
&str
может заглянуть внутрь String
, поскольку он может указывать на некоторый строковый литерал. Следующий код должен скопировать литеральную строку в управляемую память String
:
let a: String = "hello rust".into();
Следующий код позволяет использовать сам литерал без копирования (только для чтения)
let a: &str = "hello rust";
Ответ 3
str
, используемый только как &str
, является строковым срезом, ссылкой на массив байтов UTF-8.
String
- это то, что раньше было ~str
, растущим, принадлежащим UTF-8 байтовым массивом.
Ответ 4
Они на самом деле совершенно разные. Во-первых, str
- это не что иное, как вещь уровня типа; это может быть рассмотрено только на уровне типа, потому что это так называемый тип с динамическим размером (DST). Размер, который занимает str
, не может быть известен во время компиляции и зависит от информации времени выполнения - он не может быть сохранен в переменной, потому что компилятору необходимо знать во время компиляции, каков размер каждой переменной. str
концептуальна просто ряд u8
байт с гарантией того, что она является действительной UTF-8. Насколько большой ряд? Никто не знает до времени выполнения, следовательно, он не может быть сохранен в переменной.
Интересно то, что &str
или любой другой указатель на str
такой как Box<str>
, существует во время выполнения. Это так называемый "жирный указатель"; это указатель с дополнительной информацией (в данном случае это размер объекта, на который он указывает), поэтому он в два раза больше. На самом деле, &str
довольно близко к String
(но не к &String
). A &str
- это два слова; один указатель на первый байт в str
и другой номер, который описывает, сколько байтов долго str
есть.
Вопреки сказанному, str
не должен быть неизменным. Если вы можете получить &mut str
в качестве эксклюзивного указателя на str
, вы можете изменить его, и все безопасные функции, которые изменяют его, гарантируют, что ограничение UTF-8 будет поддержано, потому что если оно нарушается, то мы имеем неопределенное поведение, как предполагает библиотека это ограничение верно и не проверяет его.
Так что же такое String
? Это три слова; два - то же, что и для &str
но оно добавляет третье слово, которое является емкостью буфера str
в куче, всегда в куче (str
не обязательно находится в куче), которым он управляет до того, как заполнится и должен перераспределить, как говорится, String
основном владеет str
; он контролирует его и может изменить его размер и перераспределить, когда сочтет нужным. Таким образом, String
, как сказано, ближе к &str
чем к str
.
Еще одна вещь - это Box<str>
; он также владеет str
и его представление во время выполнения такое же, как &str
но он также владеет str
отличие от &str
но не может изменить его размер, потому что не знает своей емкости, поэтому в основном Box<str>
можно рассматривать как фиксированный Длина String
, размер которой нельзя изменить (вы всегда можете преобразовать ее в String
если хотите изменить ее размер).
Очень похожая связь существует между [T]
и Vec<T>
за исключением того, что нет ограничения UTF-8, и он может содержать любой тип, размер которого не является динамическим.
Использование str
на уровне типов главным образом для создания общих абстракций с &str
; он существует на уровне типов, чтобы можно было удобно писать черты. В теории str
как тип вещь не должна существовать и только &str
но это будет означать, что нужно будет написать много дополнительного кода, который теперь может быть универсальным.
&str
очень полезно иметь возможность иметь несколько разных подстрок String
без необходимости копировать; как сказано, String
владеет str
в куче, которой он управляет, и если бы вы могли создать только подстроку String
с новой String
ее пришлось бы скопировать, потому что у всего в Rust может быть только один единственный владелец, чтобы иметь дело с безопасностью памяти. Так, например, вы можете нарезать строку:
let string: String = "a string".to_string();
let substring1: &str = &string[1..3];
let substring2: &str = &string[2..4];
У нас есть две разные подстроки str
одной и той же строки. string
- это та, которая владеет фактическим полным буфером str
в куче, а подстроки &str
являются просто жирными указателями на этот буфер в куче.
Ответ 5
Проще говоря, String
- это тип данных, который хранится в куче (как и в Vec
), и у вас есть доступ к этому местоположению.
&str
является типом среза. Это означает, что это просто ссылка на уже существующую String
где-то в куче.
&str
не выполняет никакого выделения во время выполнения. Таким образом, по соображениям памяти вы можете использовать &str
вместо String
. Но имейте в виду, что при использовании &str
вам, возможно, придется иметь дело с явными временами жизни.
Ответ 6
std::String
- это просто вектор u8
. Вы можете найти его определение в исходном коде. Это куча выделенных и растущих.
#[derive(PartialOrd, Eq, Ord)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct String {
vec: Vec<u8>,
}
str
- примитивный тип, также называемый срезом строки. Срез строки имеет фиксированный размер. Литеральная строка типа let test = "hello world"
имеет тип &'static str
. test
- это ссылка на эту статически размещенную строку. &str
нельзя изменить, например,
let mut word = "hello world";
word[0] = 's';
word.push('\n');
str
имеет изменяемый фрагмент &mut str
, например: pub fn split_at_mut(&mut self, mid: usize) → (&mut str, &mut str)
let mut s = "Per Martin-Löf".to_string();
{
let (first, last) = s.split_at_mut(3);
first.make_ascii_uppercase();
assert_eq!("PER", first);
assert_eq!(" Martin-Löf", last);
}
assert_eq!("PER Martin-Löf", s);
Но небольшое изменение в UTF-8 может изменить длину его байта, и срез не может перераспределить свой референт.
Ответ 7
Для людей на С# и Java:
- Rust '
String
===StringBuilder
- Rust
&str
=== (неизменяемая) строка
Мне нравится думать о &str
как о представлении строки, как о интернированной строке в Java/С#, где вы не можете изменить ее, только создать новую.
Ответ 8
В Rust просто String
и str
имеют разную изменчивость и неизменность соответственно.
Ответ 9
Вот быстрое и простое объяснение.
String
- расширяемая, доступная структура данных, выделенная кучей. Это может быть приведено к &str
.
str
- это (теперь, по мере развития Rust) изменяемая строка фиксированной длины, которая живет в куче или в двоичном файле. Вы можете взаимодействовать с str
как заимствованным типом только через представление среза строки, например &str
.
Особенности использования:
Предпочитайте String
если хотите владеть или изменять строку - например, передать строку в другой поток и т.д.
Выберите &str
если вы хотите, чтобы строка была доступна только для чтения.
Ответ 10
Строка является вектором char, вы можете получить к нему доступ и изменить str.