Я не понимаю разницу между ломтиком и эталоном (ржавчина) - PullRequest
0 голосов
/ 11 апреля 2020

Я новичок в ржавчине. Я не понимаю разницу между ломтиком и эталоном в ржавчине. В чем разница между & String и & str? В Интернете я читал некоторые материалы, в которых говорилось, что ссылка - это тонкий указатель, а срез - толстый указатель, но я не знаю и, похоже, не могу понять, что означают эти два. Также я знаю, что срез может стать ссылкой, но как он это делает? Что такое черта разрыва?

Ответы [ 2 ]

4 голосов
/ 11 апреля 2020

В Rust срез представляет собой непрерывный блок однородно типизированных данных различной длины.

Что это значит?

  • [u8] - это срез. В памяти это блок u8 с. Сам срез является данными. Однако часто люди называют &[u8] срезом. &[u8] - это указатель на этого блока данных. Этот указатель содержит две вещи: указатель на сами данные и длину данных. Поскольку он содержит две вещи, он называется жирным указателем. &u8 также является ссылкой (также может рассматриваться как указатель в данном случае *), но мы уже знаем, что на что бы он ни указывал, будет один u8. Следовательно, это тонкий указатель, поскольку он имеет только один элемент.

    Вам гарантировано, что все данные в [u8] имеют тип u8.

    Поскольку ваш [u8] только что определен как непрерывный блок памяти типа u8, не существует определения времени компиляции относительно его размера. Следовательно, нам нужно , чтобы сохранить его длину в указателе на него. Мы также не можем поместить его в стек (это означает: у нас не может быть локальной переменной, которая является просто [u8] **).

Расширение:

  • A [T] - это срез T с. Для любого данного T, пока T сам по себе является типом с размером ***, мы можем представить тип [T].
  • A str - это фрагмент строки. Он гарантированно будет действительным текстом UTF-8, и это все, что отделяет его от [u8]. Rust мог сбросить действительную гарантию UTF-8 и просто определить все остальное в str как часть [u8].

Ну, поскольку вы не можете владеть срезом локально ****, вам может быть интересно, как мы создаем срезы.

Ответ таков: мы помещаем данные во что-то уже известного размера, а , а затем заимствуем фрагменты из этого.

Возьмем, к примеру:

let my_array: [u32; 3] = [1, 2, 3];

Мы можем нарезать my_array на [u32] примерно так:

let my_slice: [u32] = my_array[..];

Но так как мы не можем владеть локальная переменная, размер которой еще не известен, мы должны поставить ее под ссылкой:

let my_slice: &[u32] = &my_array[..];

Суть среза в том, что это очень гибкий (за исключением времени жизни) метод работы с непрерывными блоками данные, независимо от того, откуда данные поступают. Я мог бы так же легко сделать my_array a Vec<u8>, который выделен в куче, и он все равно работал бы.

В чем разница между & String и & str?

&String является ссылкой на весь строковый объект. Строковый объект в Rust по сути является Vec<u8>. Vec содержит указатель на данные, которые он «содержит», поэтому ваш &String может рассматриваться как &&str. И именно поэтому мы можем сделать одно из следующих действий:

let my_string: String = "Abc".to_string();

let my_str: &str = &my_string[..]; // As explained previously
// OR
let my_str: &str = &*my_string;

Объяснение этого приводит меня к вашему последнему вопросу:

Что такое черта разыменования?

Черта Deref - это черта, которая описывает оператор разыменования (*). Как вы видели выше, я смог сделать *my_string. Это потому, что String реализует Deref, что позволяет разыменовывать String. Точно так же я могу разыменовать Vec<T> в [T].

Обратите внимание, что черта Deref используется в большем количестве мест, чем просто *:

let my_string: String = "Abc".to_string();

let my_str: &str = &my_string;

Если я пытаюсь присвоить значение типа &T в место типа &U, тогда Rust будет пытаться разыменовать мой T столько раз, сколько потребуется, чтобы получить U, сохраняя при этом хотя бы одну ссылку. Точно так же, если бы у меня был &&&&....&&&&T, и я попытался присвоить его &&&&....&&&&U, он все равно работал бы.

Этот называется принудительным приведением в действие: автоматическое включение &T в &U, где некоторое количество *T приведет к U.


  • *: необработанные указатели *const T и *mut T имеют тот же размер, что и ссылки, но компилятор считает их непрозрачными. Компилятор не дает никаких гарантий относительно того, что находится за необработанным указателем, или что они даже правильно выровнены. Следовательно, они небезопасны для разыменования. Но поскольку черта Deref определяет метод deref, который является безопасным, разыменование необработанного указателя является специальным и также не будет выполняться автоматически.
  • **: сюда входят и другие типы динамического размера, например в качестве признаков и extern type s. Это также включает в себя struct s, которые содержат динамически изменяемый тип в качестве своего последнего члена, хотя их очень трудно правильно построить, но в будущем это станет легче с чертой CoerceUnsized. Можно сделать все это недействительным (за исключением extern type s) с использованием ночной функции unsized_locals, которая позволяет некоторое использование локально динамически изменяемых размеров.
  • ***: все типы с типом размера чей размер известен во время компиляции. Вы можете идентифицировать их в общем; учитывая тип T, размер T известен во время компиляции, если T: Sized. Если T: ?Sized, то его размер может быть неизвестен во время компиляции (T: ?Sized является наиболее гибким требованием для вызывающих, так как он принимает что угодно ). Поскольку для среза требуется, чтобы данные внутри были смежными и однородными по размеру и типу, типы динамического размера (или !Sized) невозможно содержать внутри среза, массива или Vec<T> и поддерживать O(1) индексация. Хотя Rust, вероятно, мог бы написать специальный код для индексации в группу динамически изменяемых типов, в настоящее время это не так.
  • ****: На самом деле вы можете иметь срез, он просто должен находиться под указателем, которому он принадлежит. Это может быть, например, Box<[T]> или Rc<[T]>. Они освободят срез самостоятельно (A Box при отбрасывании и Rc, когда отбрасываются все сильные и слабые ссылки Rc (Деструктор значения вызывается, когда отбрасываются все сильные ссылки, но память не освобождается, пока не исчезнут все слабые ссылки.)).
0 голосов
/ 11 апреля 2020

Что такое ссылка

Ссылка похожа на указатель из C (который представляет ячейку памяти), но ссылки никогда не являются недействительными * (т. Е. Null), и вы не можете делать арифметику указателей c по ссылкам. Ссылки Rust очень похожи на ссылки C ++. Одна важная мотивирующая причина для использования ссылок - избегать move использования или clone ввода переменных. Допустим, у вас есть функция, которая вычисляет сумму вектора (примечание: это игрушечный пример, правильный способ получить сумму вектора nums.iter().sum())

fn sum(nums: Vec<u32>) -> Option<u32> {
    if nums.len() == 0 {
        return None;
    }
    let mut sum = 0;
    for num in nums {
        sum += num;
    }
    Some(sum);
}

, эта функция перемещает вектор, поэтому он непригоден для последующего использования.

let nums = vec!(1,2,3,4,5);
assert_eq!(sum(nums), 15);
assert_eq!(nums[0], 1); //<-- error, nums was moved when we calculated sum

решение - передать ссылку на вектор

fn sum(nums: &Vec<u32>) -> Option<u32> {
...
}
let nums = vec!(1,2,3,4,5);
assert_eq!(sum(&nums), 15);
assert_eq!(nums[0], 1); // <-- it works!

Что такое срез

A slice - это «вид на [непрерывный] блок памяти, представленный в виде указателя и длины». Его можно рассматривать как ссылку на массив (или похожую на массив вещь). Частью гарантии безопасности Rust является гарантия того, что вы не получите доступ к элементам за концом массива. Для этого sh срезы представляются внутри как указатель и длина. Это жир по сравнению с указателями, которые не содержат информации о длине. Аналогично приведенному выше примеру суммы, если бы nums был массивом, а не Vec, вы бы передали срез в sum(), а не сам массив.

Строка vs str

A str - это массив кодированных символов utf-8 , а &str - это фрагмент символов, закодированных в utf-8. String - это Vec символов в кодировке utf-8, а String реализует Deref<Target=str>, что означает, что &String ведет себя очень похоже (принудительно) на &str. Это похоже на то, как &Vec<u32> ведет себя так же, как &[u32] (Ve c реализует Deref<Target=[T]>)


*, если не сделано недействительным из-за небезопасной ржавчины

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...