Рекурсивный тип функции

В Обсуждение Rob Pike о лексическом сканировании в Go, он определяет тип функции stateFn, который возвращает еще один stateFn, например:

type stateFn func() stateFn

В попытке сделать что-то подобное в Rust я пробовал это:

type stateFn = fn() -> stateFn;

но компилятор жалуется на "незаконный рекурсивный тип", вставляя перечисление или структуру в цикл, если это необходимо ".

Могу ли я сделать это в Rust, и если да, то как?

Ответ 1

Вы можете обернуть тип функции номинальным типом (т.е. структурой или перечислением). На самом деле это то, что делает код Go: type T U определяет новый, отличный тип T, который не имеет прямого взаимозаменяемости с U, тогда как Rust type является просто псевдонимом, например type в Haskell и typedef в C.

Итак, можно написать:

struct StateFn(fn() -> Option<StateFn>);

или

struct StateFn {
    f: fn() -> Option<StateFn>
}

(мне пришлось добавить Option, потому что Go func может быть nil, в то время как Rust удаляет обнуление по умолчанию, заставляя его отказаться.)

Тем не менее, я подозреваю, что func является замыканием в Go (может хранить некоторое внутреннее состояние), а fn в Rust - это просто указатель на функцию (вообще не состояние), поэтому вы можете использовать закрытие в Rust тоже. Это можно сделать, заменив fn() -> Option<StateFn> на Box<Fn() -> Option<StateFn>> и создав его с помощью Box::new(move || { /* code here */ }).

Можно также использовать FnMut вместо fn, что дает вам большую гибкость или даже FnOnce, который представляет собой закрытие, которое можно вызвать только один раз. Каждое из этих мест последовательно увеличивает количество ограничений для вызывающего абонента, но при этом закрытие становится более гибким. (Тем не менее, проблема "безопасности объектов" означает, что Box<FnOnce> не работает в данный момент, "Очистка proc" содержит более подробную информацию и обход.)

struct StateFn {
    f: Box<FnMut() -> Option<StateFn>>
}

Синтаксический анализ для любой из этих ситуаций может выглядеть так:

let mut state_fn = Some(initial_fn);
while let Some(mut f) = state_fn {
    state_fn = (*f.f)()
}