递归函数类型

Jam*_*mes 10 rust

Rob Pike谈到 Go中的词法扫描时,他定义了一个stateFn返回另一个的函数类型stateFn,如下所示:

type stateFn func() stateFn
Run Code Online (Sandbox Code Playgroud)

为了尝试在Rust中做类似的事情,我尝试了这个:

type stateFn = fn() -> stateFn;
Run Code Online (Sandbox Code Playgroud)

但编译器抱怨"非法递归类型;如果需要,在循环中插入枚举或结构".

我可以在Rust中执行此操作,如果是这样,怎么做?

huo*_*uon 15

您可以将函数类型包装为名义类型(即结构或枚举).这实际上是Go代码正在做的事情:type T U定义一个T不能直接互换的新的,不同的类型U,而Rust type只是一个别名,就像type在Haskell和typedefC中一样.

所以,有人可能写道:

struct StateFn(fn() -> Option<StateFn>);
Run Code Online (Sandbox Code Playgroud)

要么

struct StateFn {
    f: fn() -> Option<StateFn>
}
Run Code Online (Sandbox Code Playgroud)

(我必须添加,Option因为Go func可以是nil,而Rust默认删除可空性,使其选择加入.)

也就是说,我怀疑这func是Go中的一个闭包(可以存储一些内部状态),而fn在Rust中只是一个函数指针(根本没有状态),所以你可能也想在Rust中使用一个闭包.有人可能会通过替换fn() -> Option<StateFn>a 并使用它来创建它.Box<Fn() -> Option<StateFn>>Box::new(move || { /* code here */ })

人们也可以使用FnMut而不是使用Fn它给你更多的灵活性,或者甚至FnOnce代表一个只能被调用一次的闭包.这些中的每一个都对呼叫者有了更多的限制,但是给予封闭本身更多的灵活性.(但是,"对象安全"问题意味着Box<FnOnce>此刻不起作用,"清除过程"有更多细节和解决方法.)

struct StateFn {
    f: Box<FnMut() -> Option<StateFn>>
}
Run Code Online (Sandbox Code Playgroud)

任何这些情况的解析循环可能如下所示:

let mut state_fn = Some(initial_fn);
while let Some(mut f) = state_fn {
    state_fn = (*f.f)()
}
Run Code Online (Sandbox Code Playgroud)

  • 不,*调用者*对“Fn”的限制最少:例如,他们可以通过“&amp;Fn()”调用它,但是“FnMut”或“FnOnce”都不能在那里调用,而“FnOnce”必须被调用按值传递并且只能调用一次(限制性更大)。另一方面,“Fn”对*被调用者*(闭包本身)施加了最多的限制,而“FnOnce”对闭包的限制最少(即“FnOnce”闭包的主体具有最大的灵活性)。正如答案所描述的,“FnOnce”是最好的,但是特征对象存在一些微妙之处,这意味着它不能直接工作。 (2认同)