В чем разница между Rust's `String` и `str`?


почему ржавчина есть String и str? В чем разница между String и str? Когда можно использовать String вместо str и наоборот? Один из них становится устаревшим?

6 205

6 ответов:

String - это динамический тип строки кучи, например Vec: используйте его, когда вам нужно иметь или изменить строковые данные.

str - это неизменяемая1 последовательность UTF-8 байт динамической длины где-то в памяти. Поскольку размер неизвестен,его можно обрабатывать только за указателем. Это значит, что str чаще всего2 появляется &str: ссылка на некоторые данные UTF-8, обычно называемые " строковым срезом "или просто"срезом". A кусочек это просто представление некоторых данных, и эти данные могут быть где угодно, например

  • в статическом хранилище: строковый литерал "foo" это &'static str. Данные зашиты в исполняемый файл и загружаются в память при запуске программы.
  • внутри кучи выделяется String:String разыменовывает к &str посмотреть на 'ы.
  • в стеке: например, следующее создает массив байтов, выделенный стеком, и затем получает просмотр этих данных, как &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 является кодировкой переменной ширины, это эффективно заставляет все strs, чтобы быть неизменными. В общем, мутация требует записи большего или меньшего количества байтов, чем было раньше (например, замена a (1 байт) с ä (2 + байт) потребуется сделать больше места в str).

2 в момент он может только появляются как &str, а типы динамического размера может позволить такие вещи, как Rc<str> для последовательности ссылок подсчитывается UTF-8 байт. Это также не может,str не совсем вписывается в схему DST отлично, так как нет версии фиксированного размера (пока).

у меня есть фон C++, и я нашел его очень полезным, чтобы думать о String и &str В C++ Условия:

  • П String как std::string; Она владеет памятью и выполняет грязную работу по управлению памятью.
  • П &str как char* (но немного более сложный); он указывает нам на начало куска таким же образом, вы можете получить указатель на содержимое std::string.

кто-нибудь из них собирается исчезнуть? Я так не думаю. Они служат двум целям:

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

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

let a: String = "hello rust".into();

следующий код позволяет использовать сам литерал без копирования (только для чтения)

let a: &str = "hello rust";

str используется только как &str, является строковым срезом, ссылкой на массив байтов UTF-8.

String это то, что раньше было ~str, растущий, принадлежащий массив байтов UTF-8.

они на самом деле совершенно разные. Во-первых,str - это не что иное, как вещь уровня типа; ее можно рассуждать только на уровне типа, потому что это так называемый тип динамического размера (DST). Размер str занимает не может быть известно во время компиляции и зависит от информации во время выполнения - он не может быть сохранен в переменной, потому что компилятор должен знать во время компиляции, каков размер каждой переменной. А str концептуально просто строка u8 байт с гарантируйте, что он формирует действительный UTF-8. Насколько велика эта строка? Никто не знает, пока время выполнения, следовательно, он не может быть сохранен в переменной.

самое интересное, что a &str или любой другой указатель на str как Box<str>тут существуют во время выполнения. Это так называемый" жирный указатель"; это указатель с дополнительной информацией (в данном случае размер вещи, на которую он указывает), поэтому он в два раза больше. В самом деле,&str довольно близко к String (но не &String). А &str - это два слова; один указатель на первый байт a str и еще одно число, которое показывает, сколько байт в элементе str есть.

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

что это String? Это три слова; два такие же, как для &str но он добавляет третье слово, которое является емкостью str буфер в куче, всегда в куче (a str Не обязательно в куче) он управляет до его заполнения и должен перераспределить. элемент String в принципе принадлежит a str как говорится; он управляет им и может изменять его размер и перераспределять его, когда он считает нужным. Так что String как сказано ближе к a &str чем str.

другое дело Box<str>; это также принадлежит str и его представление во время выполнения совпадает с &str, но он также принадлежит str в отличие от &str но он не может изменить его размер, потому что он не знает его емкость так в основном a Box<str> можно рассматривать как фиксированную длину String это не может быть изменено (вы всегда можете преобразовать его в String если вы хотите изменить его размер).

A очень похожие отношения существуют между [T] и Vec<T> за исключением того, что нет ограничения UTF-8, и он может содержать любой тип, размер которого не является динамическим.

использование str на уровне типа, в основном, для создания родовых абстракций с &str; Она существует на уровне типа, чтобы иметь возможность удобно писать черты. В теории str как тип вещь не должна существовать и только &str но это означало бы, что нужно будет написать много дополнительного кода, который теперь может быть родовой.

&str очень полезно иметь возможность иметь несколько разных подстрок String без необходимости копировать; как сказал Stringпринадлежит the str в куче он управляет, и если бы вы могли только создать подстроку a String новая String он должен быть скопирован, потому что все в Rust может иметь только одного владельца, чтобы иметь дело с безопасностью памяти. Так, например, вы можете нарезать строку:

let string: String   = "a string".to_string();
let substring1: &str = &string[1..3];
let substring2: &str = &string[2..4];

у нас есть два разных подстрока strs той же строки. string это тот, который владеет фактическим полным str буфер в куче и &str подстроки-это просто жирные указатели на этот буфер в куче.

проще говоря,String тип данных хранится в куче так же, как Vec и у вас есть доступ к указатель на это место.

&str тип среза. Это означает, что это просто ссылка на уже существующий String где-то в куче.

&str не делает никакого выделения во время выполнения. Итак, по соображениям памяти вы можете использовать &str over String. Но, имейте в виду, что при использовании &str возможно, вам придется иметь дело с явными жизнями.

строка-это вектор char, вы можете получить к нему доступ и изменить ул. незыблемы