北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

トレイト境界の F: FnMut(u8) -> bool という記法について

rust で where の中でトレイト境界を書ける。そして、クロージャはトレイトで実現されている。ってことなんだけど、クロージャを表す型変数のトレイト境界の書き方はなんとも気持ちが悪い。

struct Cacher<T>
    where T: Fn(u32) -> u32
{
    calculation: T,
    value: Option<u32>,
}

Closures: Anonymous Functions that Can Capture Their Environment - The Rust Programming Language

トレイト境界と言えば T: Display + Clone とか、ジェネリックであれば <> を使って T: AsRef<str> みたいな記法になるはずだが、 () とか -> とはなんなのか。

答えから言うと、これは Fn ファミリー独自の記法のようだ。文法的にはこうなっている。 ->() を使う規則は TypePathFn命名されている。

TypePathFn :
( TypePathFnInputs? ) (-> Type)?

TypePathFnInputs :
Type (, Type)* ,?

Paths - The Rust Reference

これは Foo<(...), Output=B> の糖衣だとする文章も残っている。

rust-rfcs/0587-fn-return-should-be-an-associated-type.md at master · nox/rust-rfcs · GitHub

ここまで来ると、ああ、やっぱりクロージャはトレイトなんだなという思いになってくるが、現行ではこの規則で脱糖してもコンパイルすることはできず、 TypePathFn の記法を使わねばならない。

error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change. Use parenthetical notation (Fn(Foo, Bar) -> Baz) instead (see issue #29625)
   --> src/main.rs:175:10
    |
175 | where F: FnMut<(u8,), Output=bool>
    |          ^^^^^^^^^^^^^^^^^^^^^^^^^