Что такое `fn () -> ()` в Rust?

Oct 10 2020

У меня есть схватывание Fn (капитальное-F) черты: Fn, FnMut, FnOnce. Я понимаю, что это черты характера и работают как черты.

Но как насчет fn(строчная-f)? В редакторах он получает другую окраску, что говорит мне, что это не черта. Его также можно использовать в некоторых местах, где другие не могут (и наоборот), хотя, похоже, в других случаях он ведет себя аналогичным образом. Я не мог найти ничего, что объясняло бы это прямо в документации.

Ответы

8 trentcl Oct 10 2020 at 22:09

Ответ prog-fh, по сути, правильный, но без нюансов. В Rust есть три типа функционально-подобных типов:

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

  2. Замыкания - это значения, похожие на функциональные элементы, но замыкания могут содержать данные: копии или ссылки на любые переменные, которые они захватывают из своей среды. Как вы уже знаете, вы создаете замыкание, используя синтаксис замыкания (|args| expression). Подобно элементам функций, замыкания имеют уникальные безымянные типы (которые компилятор отображает что-то вроде[closure@src/main.rs:4:11: 4:23]).

  3. Указатели функций - это то, о чем вы спрашиваете: типы, на которые они похожиfn() -> (). Указатели функций не могут содержать данные, но они не нулевого размера; как следует из их названия, они являются указателями. Указатель функции может указывать либо на элемент функции, либо на замыкание, которое ничего не захватывает, но не может быть нулевым.

Функциональные элементы и замыкания автоматически приводятся к соответствующему типу указателя функции, когда это возможно, поэтому это let f: fn(i32) = |_| ();работает: поскольку замыкание ничего не захватывает, оно может быть принудительно приведено к указателю функции.

Все три функции, такие как типы реализации соответствующей Fn, FnMutи FnOnceчерты характера ( за исключением того, что закрытия не может реализовать Fnили в FnMutзависимости от того, что они захвата). Функциональные элементы и указатели на функции также реализуют Copy, Clone, Sendи Sync(укупорочные только реализовать эти черты , когда все их содержимое делают).

С точки зрения производительности указатели на функции являются чем-то вроде компромисса между универсальными типами и типажными объектами. Для их вызова необходимо разыменовать их, поэтому вызов указателя функции может быть медленнее, чем прямой вызов элемента функции или закрытия, но все же быстрее, чем вызов dyn Fnобъекта-признака, который включает поиск vtable в дополнение к косвенному вызову.

Рекомендации

  • В чем практическая разница между элементом fn и указателем fn?
  • Зачем создавать язык с уникальными анонимными типами?
  • Как создать структуру для FFI, содержащую указатель на функцию, допускающий значение NULL?
  • Почему передача закрытия функции, которая принимает указатель на функцию, не работает?
3 prog-fh Oct 10 2020 at 21:07

Это тип указателя на функцию .

Он относится только к функции, а не к замыканию, поскольку содержит только адрес функции, а не захваченное окружение, которое требуется замыканию.

FnЧерта (капитал F) может относиться либо к закрытию или функции.

NickDecroos Oct 10 2020 at 21:07

fn - это тип указателя на функцию. См. Также здесь в документации:https://doc.rust-lang.org/std/primitive.fn.html