В чем разница между срезом и массивом?

Почему оба примера &[u8] и &[u8; 3] ok в этом примере?

fn main() {
    let x: &[u8] = &[1u8, 2, 3];
    println!("{:?}", x);

    let y: &[u8; 3] = &[1u8, 2, 3];
    println!("{:?}", y);
}

ИЗМЕНИТЬ

Тот факт, что &[T; n] может принуждать к &[T], является аспектом, который делает их допустимыми. - Крис Морган

Почему &[T; n] принуждает к &[T]? В каких других условиях это принуждение происходит?

Ответы

Ответ 1

[T; n] - это массив длиной n, представленный как n смежные экземпляры T.

&[T; n] является чисто ссылкой на этот массив, представленный как тонкий указатель на данные.

[T] - это срез, нестандартный тип; его можно использовать только в какой-то форме косвенности.

&[T], называемый срезом, является типом размера. Это толстый указатель, представленный как указатель на первый элемент и длину среза.

Таким образом, массивы имеют длину, известную во время компиляции, тогда как длины срезов являются временем выполнения. Массивы являются гражданами второго сорта в настоящее время в Rust, так как невозможно сформировать массивы массивов, и поэтому есть ручные реализации различных признаков для [T; 0], [T; 1], & c., Обычно до 32; из-за этого ограничения, ломтики гораздо более полезны. Тот факт, что &[T; n] может принуждать к &[T], является тем аспектом, который делает их допустимыми.

Существует реализация fmt::Debug для [T; 3], где T реализует Debug, а другая для &T, где T реализует fmt::Debug, и так как u8 реализует Debug, &[u8; 3] также делает.

Почему &[T; n] может принудительно использовать &[T]? В Русте, когда происходит принуждение?

Он будет принуждать, когда ему это нужно, и ни в какое другое время. Я могу представить два случая:

  • где что-то ожидает &[T], и вы даете ему &[T; n], он будет принудительно принуждать;
  • когда вы вызываете x.starts_with(…) на [T; n], он будет наблюдать, что на [T; n] такого метода нет, и поэтому autoref вступает в игру и пытается &[T; n], что не помогает, а затем принуждение play, и он пытается &[T], у которого есть метод под названием starts_with.

Фрагмент [1, 2, 3].starts_with(&[1, 2]) демонстрирует оба.

Ответ 2

Почему &[T; n] принуждает к &[T]?

Другой ответ объясняет, почему &[T; n] должен принуждать к &[T], здесь я объясню, как компилятор работает, &[T; n] может принуждать к &[T].

Есть четыре возможных принуждения в Rust:

  • транзитивность.

    • Если T приближается к U и U приводит к V, то T приближается к V.
  • Ослабление указателя:

    • удаление изменчивости: &mut T&T и *mut T*const T
    • преобразование в исходный указатель: &mut T*mut T и &T*const T
  • Deref trait:

    • Если T: Deref<Target=U>, то &T приводит к &U с помощью метода deref()
    • (Аналогично, если T: DerefMut, то &mut T приближается к &mut U через deref_mut())
  • Unsize trait:

    • Если Ptr является "типом указателя" (например, &T, *mut T, Box, Rc и т.д.) и T: Unsize<U>, то Ptr<T> приближается к Ptr<U>.

    • Функция Unsize автоматически реализуется для:

      • [T; n]: Unsize<[T]>
      • T: Unsize<Trait> где T: Trait
      • struct Foo<…> { …, field: T }: Unsize< struct Foo<…> { …, field: U }>, при условии, что T: Unsize<U> (и еще несколько условий для упрощения работы компилятора)
    • (Rust распознает Ptr<X> как "тип указателя", если он реализует CoerceUnsized. Таким образом, as ", если T: CoerceUnsized<U>, то T приближается к U".)

Причина &[T; n] заключается в следующем: &[T] является правилом 4: (a) компилятор генерирует реализацию impl Unsize<[T]> for [T; n] для каждого [T; n], и (b) ссылка &X является типом указателя. Используя их, &[T; n] может принуждать к &[T].