Почему бокс массива указателей на функции с синтаксисом `box` работает только с временным связыванием` let`? - PullRequest
0 голосов
/ 11 февраля 2019

У меня есть две фиктивные функции:

fn foo() -> u32 { 3 }
fn bar() -> u32 { 7 }

И я хочу создать коробочный фрагмент указателя функции: Box<[fn() -> u32]>.Я хочу сделать это с помощью синтаксиса box (я знаю, что это не обязательно для двух элементов, но мой реальный вариант использования отличается).

Я пробовал несколько вещей ( Playground ):

// Version A
let b = box [foo, bar] as Box<[_]>;

// Version B
let tmp = [foo, bar];
let b = box tmp as Box<[_]>;

// Version C
let b = Box::new([foo, bar]) as Box<[_]>;

Версии B и C работают нормально (C не будет работать для меня, хотя, поскольку он использует Box::new), но ошибки версии A:

error[E0605]: non-primitive cast: `std::boxed::Box<[fn() -> u32; 2]>` as `std::boxed::Box<[fn() -> u32 {foo}]>`
 --> src/main.rs:8:13
  |
8 |     let b = box [foo, bar] as Box<[_]>;
  |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait

Очевидно,по какой-то причине в версии A компилятор не может принудительно привести элементы функции к указателям на функции. Почему это?И почему он работает с дополнительной временной привязкой let?


Этот вопрос был вдохновлен другим вопросом .Мне стало интересно, почему vec![foo, bar] ошибки, но [foo, bar] работает нормально.Я посмотрел на определение vec![] и нашел эту часть, которая смутила меня.

Ответы [ 2 ]

0 голосов
/ 11 февраля 2019

Для меня это выглядит как особенность алгоритма вывода типов, и, вероятно, нет более глубокой причины для этого, за исключением того, что текущий алгоритм вывода работает так, как он.Не существует формальной спецификации того, когда вывод типа работает, а когда нет.Если вы столкнулись с ситуацией, которую механизм вывода типов не может обработать, вам нужно добавить аннотации типов или переписать код таким образом, чтобы компилятор мог правильно выводить типы, и это именно то, что вам нужно сделать здесь.

Каждая функция в Rust имеет свой отдельный тип элемента функции , который не может быть напрямую назван синтаксисом, но отображается как, например, fn() -> u32 {foo} в сообщениях об ошибках.Существует специальное принуждение, которое преобразует типы элементов функции с идентичными сигнатурами в соответствующий тип указателя функции, если они встречаются в разных ветвях match, в разных ветвях if или в разных элементах массива.Это принуждение отличается от других принуждений, так как оно происходит не только в явно типизированном контексте («узлы принуждения»), и эта специальная обработка является вероятной причиной этой идиосинкразии.

Специальное принуждение инициируетсяпривязка

let tmp = [foo, bar];

, поэтому тип tmp полностью определяется как [fn() -> u32; 2].Однако, кажется, что специальное приведение не срабатывает достаточно рано в алгоритме вывода типов при записи

let b = box [foo, bar] as Box<[_]>;

Компилятор сначала предполагает, что тип элемента массива - это тип его первого элемента, и, очевидно, при попыткеЧтобы определить, что здесь обозначает _, компилятор все еще не обновил это понятие - согласно сообщению об ошибке, _ выводится как fn() -> u32 {foo} здесь.Интересно, что компилятор уже правильно определил полный тип box [foo, bar] при печати сообщения об ошибке, поэтому поведение действительно довольно странное.Полное объяснение может быть дано только при подробном рассмотрении исходных текстов компилятора.

Механизм решателя типов Rust часто не может справиться с ситуациями, которые теоретически он должен решать. меловой движок Niko Matsakis призван обеспечить общее решение для всех этих случаев в какой-то момент в будущем, но я не знаю, каков статус и сроки этого проекта.

0 голосов
/ 11 февраля 2019

[T; N] до [T] является принудительным изменением размера .

CoerceUnsized<Pointer<U>> for Pointer<T> where T: Unsize<U> реализовано для всех типов указателей (включая интеллектуальные указатели, такие как Box и Rc).Unsize реализуется только автоматически и позволяет следующие преобразования:

  • [T; n] => [T]

Эти приведения происходят только при определенных сайты принуждения :

принуждения происходят в месте принуждения.Любое местоположение, которое явно напечатано, приведет к его типу.Если вывод необходим, принуждение не будет выполнено.В качестве исчерпывающей информации, узлами приведения выражения e к типу U являются:

  • let операторов, статик и констант: let x: U = e
  • Аргументы функций: takes_a_U(e)
  • Любое выражение, которое будет возвращено: fn foo() -> U { e }
  • Литералы структуры: Foo { some_u: e }
  • Литералы массива: let x: [U; 10] = [e, ..]
  • Литералы кортежа:let x: (U, ..) = (e, ..)
  • Последнее выражение в блоке: let x: U = { ..; e }

Ваш регистр B является оператором let, ваш регистр C является аргументом функции.Ваш случай А. не охвачен.


Если исходить из чистого инстинкта, я бы отметил, что box - это нестабильное волшебное ключевое слово, поэтому возможно, что оно наполовину реализовано.Возможно, в должны быть применены принуждения к аргументу, но никто не нуждался в этом, и поэтому он никогда не был поддержан.

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