PNGを端末に表示するプログラムを作りました。
使い方
cargo run /Path/to/Windows_logo.png
とすると、👇のように端末に画像が表示されます。透過画像も表示できます。
ちなみに、PNGの画像形式は何パターンかあるのですが、そのすべてに対応してるわけではないので、表示できない画像もあります。あしからず。
メイキング
PNGの規格が理解できたら、あとは流れで作れると思うので、PNGの規格を理解しましょう。 PNGの規格はRFCで公開されているので、それを読んでください。
...というわけにも行かないので、解説記事を読んで下さい 👇
とはいえ、RFCの英語は簡単だし読みやすいので、読むのもそこまで大変ではないと思います。
なぜやったのか
mixiのインターン期間中に開催された「インターン生vs社員LT大会」で、インターン生の人がzip圧縮に関する発表をしていました。その人は、zipの規格を読んでそれを元にzip圧縮するソフトをC++で実装したそうです。
なんでわざわざC++でやったんですか?とその人に聞いたところ、バイト列の扱いがLLなどに比べてやりやすいからだという返事が返ってきました。
なるほどな〜やっぱり人間はC++を書かなきゃいけないんやな〜ってその時は思ったんですが、インターンが終わるぐらいのときに、Rustはそういう低レイヤーなものを書くのに向いているのではないか、と思うようになりました。
そういうお気持ちだったときに、「ディジタル情報処理」という授業の先生が「ディジタル情報処理に関係するプログラム書いたら単位出すで」と言っていたので、いっちょうやったるかという機運が高まり、プログラムが作成されました*1*2。
Rustに関する感想
バイト列の取り扱いはいい感じだった
結論から言うと、Rustでバイト列を取り扱うのは結構やりやすいです*3。
例えば、u8
の配列から4つづつ読み出して、u32
と見做して値を取得したいというような課題があるわけです。
そういうとき、Rustにはbyteorderというcrateがあって、👇のようにして数値に変換できます。
extern crate byteorder; use byteorder::{BigEndian, ReadBytesExt}; fn main() { let bytes: Vec<u8> = vec![0,0,1,1, 0,0,1,2]; println!("{}", (&bytes[0..4]).read_u32::<BigEndian>().unwrap()); // => 257 println!("{}", (&bytes[4..8]).read_u32::<BigEndian>().unwrap()); // => 258 }
こういうcrateがあって、そこそこ人気がある(githubで200スターぐらい)っていうことは、低レイヤーな処理をRustにやらせたいという需要はやはり一定あるのかな、と感じます。
他にも、Rustにはプリミティブな数値型がたくさんあって、用途に応じて使い分けられるのは良いと思いました。
また、オーバーフローが起きたときにエラーを出してくれるのですが、各数値型にはwrapping_add
のようなメソッドが生えておりまして、このメソッドを通して計算をすると、オーバーフローが起こったときにスルーしてくれます。こういうのが明示的に書けるのは良いですね。
fn main() { let a: u8 = 200; let b: u8 = 200; let c: u8 = a + b; // => panic! let c: u8 = a.wrapping_add(b); // => 144 }
Rustの学習コストはやっぱり高い
一方で、やはりRustの学習コストは高いなぁと思いました。
僕自身はライフタイムや型システムを(一応)理解しているので、謎のコンパイルエラーに悩まされるという初心者あるある現象が発生することはなかったですが、move
を避けるために.clone()
を乱発するコードになってしまい、メモリをうまく使えていないなあという気持ちになりました。
まとめ
車輪の再発明を通して学んでいきたい。