В чем разница между срезом и массивом?
Почему оба примера &[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]
.