Music Player Daemon(MPD)で遊んでみる5 [Rustを愛してみる]

自分で volumio や moode みたいな物をつくるにあたって、前回では、MPDのクライアントである ncmpcpp を試してみました。 よく作りこんであるなぁと感心しました。

それらのような完成度の高い物を自分で作れるのかどうかはさておき、何がしか簡単な物を作って手ごたえを得たいなぁと思い立ちました。

長い事 C++PythonJava に、お世話になってました。 ですが、最近噂の Rust と Go がすごく気になっており、ちょっと 使ってみたいなぁと思い、どーせつくるならと、チャレンジしてみることにしました。

Rust って...

私がいまさら Rust (や他の言語)のことを紹介するまでもなく、Google先生にお伺いを立てれば、いくらでもそのような記事が世の中にはあるわけですが、私風でちょっとだけ特徴を並べてみます。

  • ビルド結果が ELF バイナリ ( Linuxの場合 )

    • よそ様の記事ではあんまり書いてないけど、早く言ってよもぉー、な事です。JavaC# のような親 ( ランタイム ) がいるのかと思っていましたが、Rustでビルドした物は、C/C++ で作ったもののように、それだけで動きます。
    • 『コアライブラリが一切の上位ライブラリ、システムライブラリ、libcライブラリにリンクしていない』ので、OS自体や、ベアメタルプログラムがつくれます。keens.github.io
  • 導入がすごく簡単、コンパイル作業が楽、ライブラリ導入が簡単で楽

    • これは、よそ様の記事にいっぱいありますので詳しくは書きませんが、本当に楽です。
    • cargo というツールがとても優秀です。
    • 色々考えなくてよいのが私にはピッタリ
  • 処理速度が速い

    • 出来上がりが普通の実行バイナリなので、Python等のスクリプト言語なんかとは比べるべくもなく高速に実行されます。
    • (機能的な観点では比べるべきではないかもしれませんが、)JavaC# なんかよりも高速に実行されるようです。
    • 実行速度が速いのが大好きな私にはピッタリ
  • ヌルポがない。

    • 言語上メモリーを参照していない変数は存在できないよーな感じになっています。
    • ヌルポがない代わりに、なにも指示していないことを表現するための型があります。Option<T>
    • 無理やりやればできます。が そんな必要はほぼ無い です。
    • でっかいプログラムつくるときはとても『安心・安全』
  • GC(ガーベージコレクター)が無い。

    • 基本的には値はスタック上で生成されること、また値に対する変数の振る舞いが、所有しているのか、参照しているのかをコンパイル時に厳しくチェックすることで、その値を破棄するタイミングがコンパイルタイムで決定的なものとなることより、ガーベージコレクターという概念自体が不要となっています。
    • 参照カウント方式でヒープを使うことが出来ます。
    • GC嫌いな私にはピッタリ
  • 静的な型付け、極めて賢い型推論

    • これも大きなプログラムを書く時にはとてもありがたいものです。
    • 関数の引数型、戻り型などより型推論できるため変数の型を指定しなくてもよいです。
    • もしくは、変数の型を指定する代わりに、関数の型を省略できます。なにげにすごい発想です。
  • 値に対して、『所有、借用』『不変性、可変性』に対して厳しい。

    • 値はたった一つの変数しか『所有』は出来ない。他の変数(関数呼び出し含む)に代入すると元の変数すら使えないです。同じ値をつかいたければ参照で『借用』することになります。
    • 単に『この変数は変更不可』『この変数は変更可能』だけのレベルではなく、もっと高度なルールがあります。一例としては、『変更不可な借用』を保持する変数が生きている限り、『変更可能な借用』をする変数を生成することは出来ません。
    • なにげにすごい発想です。 上記のルールをコンパイルタイムで厳しくチェックして、エラーにします。
    • これに慣れるのがちょっとめんどくさい
  • オブジェクト指向ではない。テンプレート指向のものはある。

    • C++ や他の言語のようなオブジェクト指向的なものはありません。
    • 代わりといっては語弊がありますが、テンプレート指向的なものはあります。Traits
    • これに慣れるのがちょっとめんどくさい
  • 非汎用ライブラリが無い

    • Pythonのような「電池付属 ("Battery Included")」では無いです。
    • 代わりに crates.ioというライブラリパッケージサイトが利用できます。
    • ちょっと残念だけど、いかにもRust的ではある
  • コンパイラ様のエラーメッセージが異常にリッチでビックリ

    • GCCなどのエラーからしてみたらすごく丁寧です。
    • エラー修正に対する修正のヒントを出してくれます。変数のタイプミスなんかも『これじゃないの?』みたいに提案してくれます。
  • 世界で愛されている*

    • 『Stack Overflowの2020年版Developer Surveyで、Rustが5年連続で最も愛されているプログラミング言語となっている』そーです。news.mynavi.jp
    • 私もやっとちょっとだけその愛がわかってきたのですが、最初はイライラして嫌いになるところでした。

『うん。Rust なかなか良いぞ。』と思った私でした。

でも、google先生に『rust 学習曲線』とお伺い立てるとわかるのですが、習得がむつかしい言語のようです。

うむ。まぁでもやってみようか。

どんなアプリを作るか

MDP に関しては、 正直 telnet があれば、大抵のことは全部できてしまいます。

でもそれでは身も蓋もありませんし、実際に telnet で操作するときに、音楽ファイルのパス名を入れるのがかなり煩わしいのです。 音楽ファイルのパス名が、どーしても長い物になりますし、パス中に空白がある場合は、" で囲むという必要があるのです。

最初のアプリなので、壮大なものは出来ません。

そーだ。Shell風にMDPを操作するアプリってのはどーだろー。

っと思い至りました。

  • コンソールで動く。
  • ls でファイルやディレクトリが見れる。
  • cdディレクトリが移動できる。
  • 短いコマンドで、再生や停止ができる。

みたいな感じを目標にしました。

どんなアプリができたか。

はい。こちら、どん!

github.com

英文とか多分しっちゃかめっちゃかだとは思うのですが、完成したのでGitHubで公開してみました。

英語なぁ....プログラムなら書けるのに、英語はなぁ...

作った上での雑感

久しぶりの新たな言語への挑戦

思えば私が以前に新しい言語を習得したのはずーっと昔でした。そこそこ色々な言語を使ってきたわけなのですが、よく使ってきた言語でいえば、

  • C
    • 30年くらい前ですか... 年がばれそーですが...そのころは 8bit 機全盛でしたね
  • C++
    • 本格的に使い始めたのは、C に対して少々あとです。
  • Python
    • 意外と付き合いは長く、20年位前から使っています。
  • Java
    • 20年位前から使っています。正直好きではなかったのですが...
  • C#
    • 7~8年くらい前かな。Javaよりはずっと好きです。

と、言ったところで最近は新しい言語を始めるなんてことは全くしていませんでした。

そーいえば、Python に関しては、

昔(私の習得したころ)は『組み込み用言語』という立ち位置だったと記憶しています、C++でできたアプリのマクロエンジン用に組み込みしたことがあります。RH7の頃からスクリプト言語として使ってきました。(ちなみに私はアンチ PHP です。Perlは嫌いじゃないけど使いたいわけでもない。)

実際 Python なんて昔はそんなに人気があったかというと全然そんなことなかったと思います。それが昨今では、『AI といえば Python』みたいなノリになるとは全然思いませんでした。正直、AI技術とPython言語は直結していないし、なんでPythonがAI分野で、もてはやされたのかもわかりませんがね。

いやはや、言語の未来なんてどーなるか分からんものですね。

それはさておき、そこそこ色々な言語で書いてきたんだから、Rustもさっさと覚えられるだろーと安易に考えていましたが、 いやぁ...やっぱり年とったからなのか、元来頭が固いからなのか、なかなかスラスラとは行きませんでした。

  • 新しい構文に対して、すんなりとタイプできない。
    • 構文自体があやふやなため、思った事をコードにするのに時間がかかりました。
    • どーしても他の言語の構文のパターンを書いてしまい、コンパイラ様に叱られます。特に if が C/C++ 風にしてみたり、Py風にしてみたり。変数宣言も型を前に書いてみたりと...。
  • Rust独特の特徴が理解できていない。
    • 『所有、借用』『不変性、可変性』に関しては、言葉にしずらい難しさがありました。
    • 他の言語であれば、これでいいのに、こーなのに、っていう固定観念がいろいろ邪魔をしてくれて、結果コンパイラ様に...
  • コンパイラ様が厳しい
    • コンパイルが通らなければ動かないのは、どの言語でも同じで、スクリプト言語でも少なからずそういう面はあるのですが、構文的正しければ動くものができ、ちょっと動かしては理解を深めるということができるのですが、Rustの場合、構文チェックの後にボローチェッカーという『所有、借用』『不変性、可変性』に対するチェックがあるため、それで問題がある間は動かすことが出来ません。

などと、結構悪戦苦闘させられました。『なんでこれだとダメなの』『どー書けば正解なの?』とコンパイラ様に悪態つきまくりでした。

既存の言語では『安全なコード』を作ることは『言語外のテクニック』だったのですが、Rustはそれを『言語仕様』にしてしまったため『安全じゃないコード』は テコでもコンパイルさせない ってことです。

『安全なコード』を作るための『言語外のテクニック』はそこそこ身についていたため、こーかけばいいんだろうな。と安易に書くと、コンパイラ様に叱られるわけで、結構イライラしてきます。

Rustの学習がむつかしいとされることをすごく実感しました。

ついでに Git、Github も初めてだった。

ちょっとはずかしいんですが、git つかったことなかったんです。ずっと Subversion のお世話になってたんで、いつかは git と思いつつ、ズルズルと...

ま、これから勉強します。

シンプルにできる?

ごくごくシンプルなコンソールアプリケーションを作ろうと思っていたわけです。最初は...

実際やりたいことはコマンド入力に対する応答ですから、複雑になる要素はあんまりないわけですからね。基本はRustを覚えることでしたから...

ですが、やっぱりそーは問屋が卸しませんでした。

  • MDP の通信部を外部ライブラリに頼ろうと思いましたが、なかなかいいのがありませんでした。あるにはあるのですが、非同期処理対応とかで敷居が高いものだったりして大袈裟なことになりそーでしたので、結局 TCP通信を自前で実装しました。
  • MDP のTCP接続に関して、何も送らないと60秒で切れちゃうため、色々考えた挙句、通信部分はスレッド実装となりました。簡単にしたかったのに...
  • readline風にしたかったので、rustyline を使ったのですが、これがまた結構大変で...
    • 自分のコードだけだったら『所有、借用』『不変性、可変性』に関しては苦労しないのですがね...外部ライブラリとかが絡むとなかなか都合つけるのが大変です。

などと、ややシンプルで済ますわけにはいかない都合がでてきてしまい、いろいろ苦労しました。まぁ大抵そういうモノですけどね。

Rustへの愛

芽生えました。ははは。

いや初めに思っていた作成時間の倍はかかりました。途中とってもイライラさせられました。なんでこんな言語仕様なのと思ったりもしました。

ですがなんとなく頭が Rust 脳になってきて、やっとコードが楽しくなってきました。

Rust いい言語ですよ。ただ素人にはおすすめできない気がします。いや無垢の素人さんのほーが、すんなり身につくかも...

Go って...

やっと Rust とお友達になれたところで、さて、Go はどーしましょーか。ネタとして、Goでも作ってみようかな。