Чому іржа String
і str
? Які відмінності між String
і str
? Коли використовується один, String
а не str
навпаки? Хтось із них стає застарілим?
Чому іржа String
і str
? Які відмінності між String
і str
? Коли використовується один, String
а не str
навпаки? Хтось із них стає застарілим?
Відповіді:
String
- це тип динамічного рядка купи, наприклад Vec
: використовуйте його, коли потрібно володіти або змінювати ваші рядкові дані.
str
є незмінною 1 послідовністю байт UTF-8 динамічної довжини десь у пам'яті. Оскільки розмір невідомий, обробляти його можна лише за вказівником. Це означає, що str
найчастіше 2 виглядає як &str
: посилання на деякі дані UTF-8, зазвичай їх називають "рядковим фрагментом" або просто "фрагментом". Фрагмент - це лише перегляд деяких даних, і ці дані можуть бути де завгодно, наприклад
"foo"
- це a &'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 А str
- фіксованої довжини; ви не можете писати байтів поза межами кінця або залишати недійсні байти. Оскільки UTF-8 є кодуванням змінної ширини, це ефективно примушує всі str
s бути непорушними у багатьох випадках. Взагалі мутація вимагає записати більше або менше байтів, ніж раніше (наприклад, заміна a
(1 байт) на ä
(2+ байтів) вимагала б більше місця в str
). Існують конкретні методи, які можуть змінювати &str
місце, в основному ті, що працюють лише з символами ASCII make_ascii_uppercase
.
2 Типи з динамічним розміром дозволяють на зразок Rc<str>
послідовності посилань рахувати UTF-8 байт з Rust 1.2. Іржа 1,21 дозволяє легко створювати ці типи.
[u8; N]
.
Rc<str>
і Arc<str>
тепер можна використовувати з допомогою стандартної бібліотеки.
У мене є 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
теперBox<str>
~str
було вирощувати, а Box<str>
не вирощувати. (Це ~str
і ~[T]
було магічно вирощуваним, на відміну від будь-якого іншого ~
-об'єкта, саме тому String
і Vec<T>
було запроваджено, так що всі правила були чіткими та послідовними.)
Вони насправді зовсім інші. По-перше, a str
- це не що інше, як річ на рівні типу; про це можна міркувати лише на рівні типу, оскільки це так званий тип динамічного розміру (DST). Розмір, який str
займає, не може бути відомий під час компіляції і залежить від інформації часу виконання - він не може зберігатися в змінній, оскільки компілятору необхідно знати під час компіляції, який розмір кожної змінної. str
Концептуально A - це лише ряд u8
байтів із гарантією того, що він утворює дійсний UTF-8. Наскільки великий ряд? Ніхто не знає, поки час його виконання не може бути збережений у змінній.
Цікаво те, що &str
і будь-який інший покажчик на str
Like Box<str>
робить EXIST під час виконання. Це так званий «жировий покажчик»; це вказівник із додатковою інформацією (в даному випадку розміром речі, на яку він вказує), тож він удвічі більший. Насправді, a &str
досить близький до String
(але не до a &String
). А &str
- це два слова; один вказівник на перший байт a str
і інший номер, який описує, скільки байтів str
дорівнює.
Всупереч сказаному, а str
не потрібно бути незмінним. Якщо ви можете отримати &mut str
ексклюзивний покажчик на str
, ви можете вимкнути його, і всі безпечні функції, які його мутують, гарантують, що обмеження UTF-8 підтримується, тому що якщо це порушено, ми маємо невизначене поведінку, оскільки бібліотека передбачає це обмеження. правда і не перевіряє на це.
Отже, що таке String
? Це три слова; два - це те саме, що і для, &str
але це додає третє слово - це ємність str
буфера на купі, завжди на купі (a str
не обов'язково на купі), якою вона управляє, перш ніж її заповнити і доведеться перерозподілити. в String
основному володіє а, str
як кажуть; він контролює його і може змінити його розмір і перерозподілити його, коли вважає за потрібне. Тож а String
, як сказано, ближче &str
до а str
.
Інша справа - це Box<str>
; цьому також належить a str
і його представлення часу виконання таке ж, як, &str
але воно також володіє на str
відміну від нього, &str
але воно не може змінити його розмір, оскільки він не знає його ємності, а в основному a Box<str>
може розглядатися як фіксована довжина, String
яку неможливо змінити (ви можете завжди конвертуйте його у формат, String
якщо ви хочете змінити його розмір).
Дуже схоже співвідношення існує між [T]
і за Vec<T>
винятком того, що немає UTF-8 , обмеження і він може містити будь-який тип, розмір якого не є динамічним.
Використання str
на рівні типу здебільшого для створення загальних абстракцій &str
; він існує на рівні типу, щоб можна було зручно писати риси. Теоретично, str
як річ типу, не потрібно було існувати і тільки, &str
але це означало б писати багато зайвого коду, який тепер може бути загальним.
&str
дуже корисно мати можливість мати декілька різних підрядків a String
без копіювання; як сказав String
володієstr
на купі вона управляє , і якщо ви можете створити тільки подстроку String
з новим String
він повинен копіюватися , тому що все в іржі може мати тільки один єдиний власник , щоб мати справу з безпекою пам'яті. Так, наприклад, ви можете нарізати рядок:
let string: String = "a string".to_string();
let substring1: &str = &string[1..3];
let substring2: &str = &string[2..4];
У нас є дві різні підрядки str
з одного рядка. string
це той, хто володіє фактичним повним str
буфером на купі, а &str
підрядки - лише жирні вказівники на цей буфер на купі.
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 може змінити довжину байтів, і фрагмент не може перерозподілити його референта.
Простими словами, String
тип даних зберігається в купі (точно так само Vec
), і ви маєте доступ до цього місця.
&str
- це тип зрізу. Це означає, що це лише посилання на вже присутнє String
десь у купі.
&str
не виконує жодного розподілу під час виконання. Отже, з міркувань пам’яті ви можете використовувати &str
понад String
. Але майте на увазі, що при використанні &str
вам, можливо, доведеться мати справу з явними термінами життя.
str
це view
з вже присутній String
в купі.
Для людей з C # та Java:
String
===StringBuilder
&str
=== (незмінний) рядокМені подобається думати про &str
вид як про рядок, як про інтерновану рядок у Java / C #, де ви не можете його змінити, лише створіть нову.
Ось швидке та просте пояснення.
String
- Зростаюча, власна структура даних, що виділяється нагромадженням. Його можна примусити до а &str
.
str
- це (зараз, як Руст розвивається) змінний рядок фіксованої довжини, що живе на купі або у двійковій. Ви можете взаємодіяти з str
типом, запозиченим, лише через вигляд фрагмента рядка, наприклад &str
.
Принципи використання:
Віддайте перевагу, String
якщо ви хочете володіти або мутувати рядок - наприклад, передача рядка в інший потік тощо.
Віддайте перевагу, &str
якщо ви хочете мати рядок лише для читання.
&str
складається з двох компонентів: покажчик на деякі байти і довжину.»