Почему вложенная ссылка на массив не приводит к срезу?


Я читаю каковы точные правила автоматического разыменования Rust? от начала до конца, но у меня все еще есть вопрос о принуждении от массива к срезу.

Давайте подумаем о следующем коде:
let arr: &[i32; 5] = &&&[1, 2, 3, 4, 5];
// let arr: &[i32] = &&&[1, 2, 3, 4, 5]; // Error; expected slice, found reference

Я ожидал бы, что &&&[1, 2, 3, 4, 5] имеет тип, &&&[i32; 5] и разыменования на &&[i32; 5] => &[i32; 5] => &[i32; 5] => &[i32], но результат оказался совсем не таким, как я ожидал.

Я попытался запустить следующий код:

let arr: &&&[i32; 5] = &&&[1, 2, 3, 4, 5];
let n = arr.first().unwrap(); // 1

Это правильный код. Тип arr принуждается к &&&[i32; 5] => &&[i32; 5] => &[i32; 5] => &[i32] и соответствует первому аргументу first в slice, &self.

Каково условие, при котором массивы принуждают к срезам? Я не понимаю разницы между первым и вторым кодом.

Я также проверил документацию в исходном коде , и предполагаю, что вышеупомянутый вопрос имеет какое-то отношение к приведенному ниже предложению;

Однако мы иногда делаем другие корректировки и принуждения попутно, в частности, безразмерность (например, преобразование из [T; n] в [T]).'

1 5

1 ответ:

Этот вид принуждения предназначен для работы, но не реализуется.

Массивы не реализуют Deref, поэтому принуждение &[T; n] -> &[T] не является принуждением deref и работает не совсем так, как один. Вместо этого он называется "принуждением DST", потому что срезы (наряду с объектами признаков) являются так называемыми типами динамического размера.

Тем не менее, ссылка на язык (которая не является нормативной и может быть устаревшей, но терпеть меня) перечисляет возможные принуждения, в том числе:
  • T_1 to T_3 где T_1 coerces to T_2 и T_2 coerces to T_3 (транзитивный случай )
  • &T к &U, Если T реализует Deref<Target = U>.
  • TyCtor (T) to TyCtor (coerce_inner (T)), где TyCtor (T) - один из

    • &T
    • &mut T
    • *const T
    • *mut T
    • Box<T>

    И где

    • coerce_inner ([T, ..n]) = [T]
    • coerce_inner(T) = U где T - конкретный тип, реализующий признак U.

    В будущем функция coerce_inner будет рекурсивно расширена на кортежи и структуры. Кроме того, принуждение от суб-черт к супер-чертам будет добавлено. Смотрите RFC 401 для получения более подробной информации.

Последняя пуля несколько плотная, но именно она позволяет &[T; n] принуждать к &[T]. Примечательно, что это описывает только один слой ссылок; она не покрывает&&[T; n] -> &[T] случай (для которого нам также нужно Deref принуждение).

Вернемся к вашему нерабочему примеру:

let arr: &[i32] = &&&[1, 2, 3, 4, 5];

Предполагаемое принуждение является &&&[i32; 5] -> &[i32]. Мы можем выяснить, как это принуждение должно работать:

  1. &[i32; 5] принуждает к &[i32] принуждением DST (последнее правило);
  2. &&[i32; 5] принуждает к &[i32; 5] по Deref (второе правило);
  3. следовательно, &&[i32; 5] согласуется с &[i32] через транзитивность (первый правило).
  4. &&&[i32; 5] coerces to &&[i32; 5] by Deref;
  5. следовательно, &&&[i32; 5] соответствует &[i32] по транзитивности.

Но это не так, потому что проблема #18602: в двух словах, транзитивные принуждения все еще не реализованы. До тех пор, пока этот вопрос не будет решен, принуждение через транзитивность невозможно. По-видимому, это не самый высокий приоритет, вероятно, потому, что массивы большого размера не очень распространены. (Я подозреваю, что это может стать более распространенной жалобой когда const generics приземляются, так как это может сделать массивы более полезными.)

Так почему же делает arr.first() работа? Итак, "правила автоматического разыменования", используемые для поиска методов, вызываемых с помощью оператора . (точка), являются расширением правил принуждения. Autoderef аналогичен разыменованию вручную любое количество раз, пока вы не получите что-то (что можно принудительно привести к типу) с помощью данного метода. Это означает, что вам не нужна транзитивность, чтобы найти вызовы метода через autoderef (который RFC 401 называет "принуждение приемника").


Дальнейшее чтение

РЧЦ #401 описание , предназначенных семантика самого приведения. Этот RFC был объединен более 3 лет назад. С тех пор многое изменилось, но он все еще не полностью реализован (его проблема отслеживания #18469), таким образом, RFC 401 не описывает точно ни прошлую, ни настоящую, ни будущую версию Rust. Тем не менее, RFC 401 также допускает принуждение &&&[i32; 5] к &[i32] и почти по той же логике.

ВRustonomicon также есть глава о принуждении и, по-видимому, согласуется со справочником.