RustでVec<&str>を返そうとして困った

人は暇になるとLispインタプリターを実装する。

そんなこんなでRustの練習がてら今はRustでLispインタプリタの実装に取り掛かっている。

基本的な要件はラムダ式が扱えてリスト処理、簡単な算術演算ができるようにすること。

前にこの仕様でPythonで実装した。

github.com

これを今回Rustでやろうというわけである。

リストをreturnする

Lispインタプリタを実装する第一歩として、トークナイザを実装することがある。

これはいたって簡単で、Lisp(function arg0 arg1 ...)というようにカッコの頭に関数がきてその後に引数が来る。

つまりトークナイザの実装としては

(func arg0 arg1) 

=> ["(", "func", "arg0", "arg1", ")"]

というように空白区切りでリストへ変換するだけの作業となる。

そんなこんなでこれをRustで実装しようとしたのだが、これにちょっと手こずった。

結論としては以下のコードを実装すれば良い。

fn tokenize(s: &str) -> Vec<String>{
    let spreaded = s.replace("(", " ( ")
                      .replace(")", " ) ");
    
    let tokens: Vec<String> = a.split(" ")
                                 .map(|item| item.to_string())
                                 .collect();

    return tokens;
}

色々試した

上記のコードで動かせばいい感じになるが、あいにくRustユーザーとしてはまだまだ未熟なので色々試行錯誤していた。

最初に書いたコードはこれ。

fn tokenize(s: &str) -> Vec<&str>{
    let tokens: Vec<&str> = s.replace("(", " ( ")
                               .replace(")", " ) ")
                               .split(" ")
                               .collect();

    return tokens;
}

これをやるとerror[E0515]: cannot return value referencing temporary valueと怒られた。

ローカル変数屁の参照を返すなボケと言っている。

これを直すために&strStringを調べてみると、&strはsliceで配列への参照、StringVectorらしい。

ということで今回のケースでは中身に文字列が入ったリスト(ベクトル)をreturnするためにVecの中の文字列の型を&strではなくStringとして記述するべきらしい。

ということで色々試行錯誤する(ここで何度も型を間違えて3時間くらい格闘した)

そんなこんなで色々ググったりして格闘してるうちに上記の動くコードを書くことができた。

反省

ずっとLispPythonという型を明示的に書かない言語を長いこと触っていなかったためにかなり手こずった。

具体的にRustで型を使いこなすにはそれなりに鍛錬が必要な気がするのでこれからはRustで色々書いていこうと思う。

ただ、今回&strStringを間違えていたらなかなかに危険なプログラムが出来上がっていただろうから、Rustの所有権のありがたみとCargoの面倒見の良さに感謝していたりもする。

Rustがんばっていきたい。