Rustコンパイラの優しさに触れながらフィボナッチ関数のクロージャを作る
きっかけ
A Tour of Goを見てたら、クロージャを用いてフィボナッチ数列を出力する例題がありました。
fibonacci()
関数の中は自分で作成する必要がある問題だったのですが、解答としてはおそらくこんな感じ。
package main import "fmt" // fibonacci is a function that returns // a function that returns an int. func fibonacci() func() int { x := 0 y := 1 return func() int { ans := x x, y = y, x+y return ans } } func main() { f := fibonacci() for i := 0; i < 10; i++ { fmt.Println(f()) } }
なんとなくPythonのジェネレータとも近い雰囲気を感じましたが、こちらはイテレータオブジェクトを生成するのでちょっと違いますね。Pythonでyield
を使って書いたのがこんな感じ。
def fib(i): x = 0 y = 1 for _ in range(i): ans = x x, y = y, x+y yield ans for v in fib(10): print(v)
Rustで書く
さて、そういえばRustでこういうの書いた覚えがないなーと思って挑戦してみました。最終的にはこうなりました。
fn fibonacci() -> impl FnMut() -> i32 { let mut x = 0; let mut y = 1; move || { let ans = x; let tmp = x; x = y; y += tmp; ans } } fn main() { let mut f = fibonacci(); for _ in 0..10 { println!("{}", f()); } }
最初は、たぶんコンパイラに怒られるだろうと、fn fibonacci() -> Fn() -> i32
なんてシグネチャで書いてみたのですが案の定怒られまして。だけどコンパイラさんが「dyn
つけろ」「FnMut
にしろ」とか丁寧に指示してくれるので、一旦は以下のような形で動作させることはできました。
fn fibonacci() -> Box<dyn FnMut() -> i32> { // ... Box::new(move || { // ... } }
でも、Box
使うのなんかやだなーと思って外してみたら「impl Trait
使えばええんやで」とコンパイラさんがまたしても教えてくれたので上記のような形になりました。
↓温かいアドバイス
= note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits> help: use `impl FnMut() -> i32` as the return type, as all return paths are of type `[closure@src/main.rs:4:5: 10:6]`, which implements `FnMut() -> i32` | 1 | fn fibonacci() -> impl FnMut() -> i32 { | ^^^^^^^^^^^^^^^^^^^
Rustコンパイラさんの優しさに涙が出そうになった夜でした。これからも推し続けます。