RustでSMFパーサーを書く

※この記事は文系的冗長性及び反知性的曖昧主義に支配されています[^1]。

# 現代楽譜学概論あるいは錆びついた頭にオイルを

Null安全[^2]が一部界隈を賑わせた2016年11月。貴重な休日を湯水のように費やして不毛な作業に明け暮れる文系日曜プログラマーである私は、案の定、天啓を得ました。蘇る記憶。30億のデバイス。ヌルポ。ガッ。もういやだ、モダンなラングエッヂをライトしたい……！[^3]

とはいえ、限られた時間で書く以上、目的をもって取り組みたいというもの。一通り思いを巡らせて浮かんだのが、C++を覚えるためにSMFパーサーを書いた経験。ビット演算からファイル入出力まで一通り必要となる上に、規模感が手頃で、新たな言語を覚えるのにそこそこ向いています。何よりちょうど、より高機能なものに書き直したいと思っていました。というわけで、C++の代替言語（と目される）RustでSMFパーサーライブラリ[`ghakuf`](https://crates.io/crates/ghakuf)を書きました。この記事は、コードの解説と公開までの道のりを記すものです。

## 立てばPC、座ればDAW、歩く姿はSMF
ところでSMF（Standard MIDI File）をご存知ですか？MIDI[^4]のファイルフォーマットの一つで、要するにデジタル音源に対応した音符と演奏記号の集合体（楽譜データ）です。「.mid」となっているファイルは十中八九これ。90年台後半に著作権を無視したSMFをやりとりする人が多発してしまったため、懐かしさを覚えるもといアンダーグラウンドな印象を受ける人がいるかもしれません。セスエムエフコワクナイ。ブンケイウソツカナイ。

SMFのファイル構造は以下のようなイメージ。バイナリデータ（≠プレーンテキスト）であることに注意。

```text
SMF
├MThd          ：4byte    ヘッダー
│├Length       ：4byte    ヘッダーのサイズ。ヘッダーは形式が決まっているので必ず６（byte）。
│├SMF Type     ：2byte    SMFのタイプ。普通は１。
│├Number Tracks：2byte    トラックの数
│└Time Division：2byte    時間単位。演奏タイミングの計算に使用。通常は正数で、４分音符あたりの分解能が入る。
├MTrk          ：4byte    指揮者トラック（Conductor Track）
│├Length       ：VLQ      トラックのサイズ
│├Meta Event              メタ情報
│├Meta Event              メタ情報
│├ ...
│└Meta Event   ：3byte    トラックの終端を示すメタ情報（End of Track）
├MTrk          ：4byte    トラック１
│├Length       ：VLQ      トラックのサイズ
│├MIDI Event              演奏データ
│├MIDI Event              演奏データ
│├MIDI Event              演奏データ
│├MIDI Event              演奏データ
│├ ...
│└Meta Event   ：3byte    トラックの終端を示すメタ情報（End of Track）
├MTrk          ：4byte    トラック２
├ ...
```
基本的には、先頭にヘッダーがあって、その後にトラックと呼ばれるデータの塊が続きます。トラックには以下の３種類のデータのいずれかが格納されています。

|イベント|概要|代表例|
|---|---|---|
|MIDIイベント|いわゆる演奏情報。ここで発声スタート！とか、ここで演奏方法を変更！とか。|ノート・オン、ポリフォニックキープレッシャー、チャンネル・チェンジ|
|メタ・イベント|演奏情報以外のメタ情報。全体の演奏スピードや楽譜全体に適用される情報とか。|テンポ、著作権情報、トラック終端|
|システム・エクスクルーシブ・イベント|音源等に送信される特殊な情報。MIDIを通して機器間の通信を行う際に用いられる。|（SMFで使われているところを見たことない……）|

このほか、[可変長数値表現（VLQ）](https://en.wikipedia.org/wiki/Variable-length_quantity)や[ランニングステータス](http://www.lim.di.unimi.it/IEEE/MIDI/SOT5.HTM#Running)が抑えておくべきポイントになりますが、以下のとおり良質な日本語の解説も多いため、詳細な仕様解説はそちらでご確認を。

* わいやぎ氏「[SMF (Standard MIDI Files) の構造](https://sites.google.com/site/yyagisite/material/smfspec)」（個人サイト、2010年10月）
* g200kg氏「[DTM技術情報](http://www.g200kg.com/jp/docs/tech/)」（個人サイト、2011年９月）
* EternalWindows氏「[サウンド / MIDI](http://eternalwindows.jp/winmm/midi/midi00.html)」（個人サイト、2006年）

## ハッカーもすなるRustといふもの

流行りそうで流行らない少し流行っているイマドキ言語。そのコンセプトは、公式ガイドブックの頭書きに集約されています。

> [プログラミング言語Rust](https://rust-lang-ja.github.io/the-rust-programming-language-ja/1.6/book/)
> Rustは安全性、速度、並行性の3つのゴールにフォーカスしたシステムプログラミング言語です。 ガーベジコレクタなしにこれらのゴールを実現していて、他の言語への埋め込み、要求された空間や時間内での動作、 デバイスドライバやオペレーティングシステムのような低レベルなコードなど他の言語が苦手とする多数のユースケースを得意とします。 全てのデータ競合を排除しつつも実行時オーバーヘッドのないコンパイル時の安全性検査を多数持ち、これらの領域をターゲットに置く既存の言語を改善します。 Rustは高級言語のような抽象化も含めた「ゼロコスト抽象化」も目標としています。 そうでありつつもなお低級言語のような精密な制御も許します。

この公式ガイドブックが非常に良くできていて、かつ最新の仕様がまとめられている[^5]ので、これだけで初心者が十分に勉強できます。その他では、Rustの感触をつかむまで以下のサイトが大変役に立ちました。

* いもす氏「[Rustは何が新しいのか（基本的な言語機能の紹介）](https://imoz.jp/note/rust-functions.html)」（個人サイト、2017年１月）
* Raphael ‘kena’ Poss氏「[関数型プログラマのためのRust](http://postd.cc/rust-for-functional-programmers/)」（個人サイト、2014年７月、POSTD訳）
* Benjamin Fry氏「[RustとDNSの1年](http://postd.cc/a-year-of-rust-and-dns/)」（個人ブログ、2016年８月、POSTD訳）

ちなみに、半年ほどRustを触って最も得心が行ったのは以下のツイート。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">rust はシステムプログラミング云々を抜きにして、メモリ管理界隈の「もう人類はGC無しじゃ無理なんじゃ?」vs「根性という名の知性でやればできる」という対立構造に対して「コンパイラで延々と殴れば人は正しい道を歩ける」という第三極を提示したのが良かった気がする。何も知らんけど。</p>&mdash; はんぺんプログラマー (@hanpen_good) <a href="https://twitter.com/hanpen_good/status/826818672018665472">2017年2月1日</a></blockquote>

Rustでプログラミングをするにあたって、エディターは[Atom](https://atom.io/)を採用しました。理由はとくにありませんが、私の開発環境がWindowsとMacなので、[Emacs](https://www.gnu.org/software/emacs/)や[Vim](http://www.vim.org/)より使いやすいかなーというくらい[^6]。参考までに、使用しているパッケージは以下のとおり。

|パッケージ名|作者|概要|
|---|---|---|
|[Atom Beautify](https://atom.io/packages/atom-beautify)|Glavin001氏|コード整形を自動で行ってくれます。Rustにも対応。|
|[Build Cargo](https://atom.io/packages/build-cargo)|（Atom公式）|Rustのビルドシステム「[Cargo](http://doc.crates.io/guide.html)」のコマンドをAtomに追加してくれます。「cargo doc --no-deps」も追加してほしい……。|
|[Language Rust](https://atom.io/packages/language-rust)|zargony氏|Rustのシンタックス・ハイライト（関数や変数といった意味に応じて色を変えるアレ）です。|
|[Linter Rust](https://atom.io/packages/linter-rust)|（Atom公式）|Rustの構文チェックをしてくれます。たまにうるさい。|
|[Racer](https://atom.io/packages/racer)|edubkendo氏|Rustのコード補完（コード入力中に、その続きの候補を出してくれるアレ）をしてくれます。事前にCargoから[Racer](https://github.com/racer-rust/racer)をインストールしておく必要があります。|

![atom.png](https://qiita-image-store.s3.amazonaws.com/0/193268/0a847e96-ea80-c398-07cd-8f5a49aff356.png)

# マクガフィンたるSMF、ときどきRustのオノマトペ

前置きが長くなりましたが、SMFパーサーをRustのライブラリとして実装するまでの道のりは以下のとおり。完成形だけ知りたい方は[レポジトリ](https://github.com/acknak/ghakuf)または[ドキュメント](https://docs.rs/ghakuf/0.3.2/ghakuf/)へどうぞ。

## おたまじゃくしは電子音符の夢を見るか？

前節で簡単に紹介したとおり、SMFは基本的に各トラックに記述されたMIDIイベント等からなるため、これをRustで表現します。

```rust
pub enum Message {
    MetaEvent { delta_time: u32, event: MetaEvent, data: Vec<u8> },
    MidiEvent { delta_time: u32, event: MidiEvent },
    SysExEvent { delta_time: u32, event: SysExEvent, data: Vec<u8> },
    TrackChange,
}
pub enum MetaEvent {
    SequenceNumber,
    TextEvent,
    // 中略
    Unknown { event_type: u8 },
}
pub enum MidiEvent {
    NoteOff { ch: u8, note: u8, velocity: u8 },
    NoteOn { ch: u8, note: u8, velocity: u8 },
    // 中略
    Unknown { ch: u8 },
}
pub enum SysExEvent {
    // 中略
}
```
各イベントにはデルタタイムと呼ばれる時間情報が記録されており、また、可変長のデータが含まれることがあるため、まずは上位の列挙型[^7]`Message`で各イベントを記述し、それぞれの詳細を下位の列挙型（`MetaEvent`、`MIDIEvent`、`SysExEvent`）で定義しています。パーサーがSMFでイベントを発見するたびに`Message`を作成します。

ちなみに`TrackChange`というイベントはSMFに存在しませんが、後述のとおりパーサーの設計をイベント駆動型にするために定義しています。

## イベントよみに与ふる書

今回は作成するのはライブラリなので、[イベント駆動型](https://ja.wikipedia.org/wiki/イベント駆動型プログラミング)[^8]を採用し、[Observerパターン](https://ja.wikipedia.org/wiki/Observer_パターン)で実装しています。Rustに[クラス](https://ja.wikipedia.org/wiki/クラスベース)や[プロトタイプ](https://ja.wikipedia.org/wiki/プロトタイプベース)は存在しないため、[トレイト](https://rust-lang-ja.github.io/the-rust-programming-language-ja/1.6/book/traits.html)でオブザーバーが満たすべき境界を実装します。

```rust
pub trait Handler {
    fn header(&mut self, format: u16, track: u16, time_base: u16) {
        let _ = (format, track, time_base);
    }
    fn meta_event(&mut self, delta_time: u32, event: &MetaEvent, data: &Vec<u8>) {
        let _ = (delta_time, event, data);
    }
    fn midi_event(&mut self, delta_time: u32, event: &MidiEvent) {
        let _ = (delta_time, event);
    }
    fn sys_ex_event(&mut self, delta_time: u32, event: &SysExEvent, data: &Vec<u8>) {
        let _ = (delta_time, event, data);
    }
    fn track_change(&mut self) {}
    fn status(&mut self) -> HandlerStatus {
        HandlerStatus::Continue
    }
}
pub enum HandlerStatus {
    Continue,
    SkipTrack,
    SkipAll,
}
```
パーサーがMIDIイベントを見つけると`fn midi_event(...)`で、メタ・イベントを見つけると`fn meta_event(...)`で、事前に登録されたオブザーバーに通知します。イベントを通知するだけではトラックの変わり目を伝えられないため、`fn track_change(...)`も用意しています。

その他、これ以上のパースが不要になった際にファイルIOをスキップするため、`fn status(...)`を通して、オブザーバーが状況を列挙型`HandlerStatus`でパーサーに伝えられるようにしています。

## 観測者にラブソングを

それはいよいよパーサーの解説。

```rust
pub struct Reader {
    file: io::BufReader<fs::File>,
    handlers: Vec<Box<Handler>>,
    path: path::PathBuf,
}
impl Reader {
    pub fn new(handler: Box<Handler>, path: &str) -> Result<Reader, ReadError> {
        let mut handlers: Vec<Box<Handler>> = Vec::new();
        handlers.push(handler);
        Ok(Reader {
            file: io::BufReader::new(fs::OpenOptions::new().read(true).open(&path)?),
            path: path::PathBuf::from(path),
            handlers: handlers,
        })
    }
    pub fn read(&mut self) -> Result<(), ReadError> {
        // 中略：Handlerの状態チェック
        self.file.seek(io::SeekFrom::Start(0))?;
        self.check_tag(Tag::Header)?;
        self.read_header_block()?;
        while self.check_tag(Tag::Track)? {
            // 中略：Handlerの状態チェック
            self.read_track_block()?;
        }
        Ok(())
    }
    // 中略
}
```
`Reader::new(...)`で観測者やSMFのファイルパスを登録するところからスタートします。`Reader`のメンバー`handlers`がベクターであることからわかるとおり、観測者は複数登録できます[^9]。準備ができれば`fn read(...)`でパース開始。ヘッダーを読み終えた後、MIDIイベント等を読むためのメインループに入ります。

ちなみに、Rustでファイル入出力を行う際はEOFチェックとバッファリングの扱いが直感（C言語）と異なるため、注意する必要があります。

* mkaito氏・Shepmaster氏「[How to check for EOF with \`read_line()\`?](https://stackoverflow.com/questions/27475113/how-to-check-for-eof-with-read-line)」（stackoverflow、2014年12月）
* gyu-don氏「[RustのファイルI/OにはBufReader, BufWriterを使いましょう、という話](http://qiita.com/gyu-don/items/50f4239fc856bed00dc4)」（Qiita記事、2017年３月）

MIDIイベント等を読むコードは以下のとおり。トラック冒頭に記述されたデータサイズがつきるまで、発見したイベントを観測者に知らせ続けます。

```rust
fn read_track_block(&mut self) -> Result<&mut Reader, ReadError> {
    let mut data_size = self.file.read_u32::<BigEndian>()?;
    while data_size > 0 {
        // 中略：Handlerの状態チェック
        let delta_time = self.read_vlq()?;
        data_size -= delta_time.len() as u32;
        let mut status = self.file.read_u8()?;
        // 中略：ランニングステータスの処理
        data_size -= size_of::<u8>() as u32;
        match status {
            0xff => {
                // meta eventの処理
                let meta_event = MetaEvent::new(self.file.read_u8()?);
                data_size -= size_of::<u8>() as u32;
                let len = self.read_vlq()?;
                let data = self.read_data(&len)?;
                data_size -= len.len() as u32 + len.val();
                for handler in &mut self.handlers {
                    if handler.status() == HandlerStatus::Continue {
                        handler.meta_event(delta_time.val(), &meta_event, &data);
                    }
                }
            }
            0x80...0xef => {
                // 中略：midi eventの処理
            }
            0xf0 | 0xf7 => {
                // 中略：system exclusive eventの処理
            }
            _ => {
                // 中略：想定外のイベントが発見された際のエラー処理
            }
        };
    }
    Ok(self)
}
```
SMFの各イベントは先頭のステータスバイトでその種類を判別するため、当該バイトを`match`で振り分けて処理しています。ところで、SMFは[ビッグエンディアン](https://ja.wikipedia.org/wiki/エンディアン)で値が格納されているため、[byteorder](https://crates.io/crates/byteorder)というクレートを使用しています。便利ですね。ちなみに、`MetaEvent::new(...)`なんてしれっと使っていますが、これは、Rustでは列挙体にメソッドを実装できるためです。その他、バイナリを作成するメソッド`fn binary(...)`等を独自に実装しています。

なお、今回はライブラリを作成しているため、エラー処理[^10]を以下のとおり少し丁寧に書いています。

```rust
pub enum ReadError {
    InvalidHeaderTag { tag: [u8; 4], path: path::PathBuf },
    InvalidIdentifyCode { code: u32, path: path::PathBuf },
    InvalidTrackTag { tag: [u8; 4], path: path::PathBuf },
    Io(io::Error),
    NoValidHandler,
    UnknownMessageStatus { status: u8, path: path::PathBuf },
}
impl fmt::Display for ReadError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use reader::ReadError::*;
        match *self {
            InvalidHeaderTag { tag, ref path } => {
                write!(
                    f,
                    "Invalid header tag '{:?}' has found: {}",
                    tag,
                    fs::canonicalize(&path).unwrap().display()
                )
            }
            // 中略
        }
    }
}
impl error::Error for ReadError {
    fn description(&self) -> &str {
        use reader::ReadError::*;
        match *self {
            InvalidHeaderTag { .. } => "（エラー文）",
            // 中略
        }
    }
}
impl From<io::Error> for ReadError {
    fn from(err: io::Error) -> ReadError {
        ReadError::Io(err)
    }
}
```
パース時に生じうる各種エラーを列挙体`ReadError`に掲げ、エラーを表現する[`error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html)トレイト及びデバッグ時の表示を定める[`std::fmt::Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html)トレイトを実装しています。また、`ReadError`のうち[`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html)は`std`モジュールに定義されるエラーですので、`ReadError`に変換する[`From`](https://doc.rust-lang.org/std/convert/trait.From.html)トレイトも実装しています。

## SMFにかける橋

ところで、せっかくパーサーを作ったのだから、書き出しも揃えたくなりませんか？というわけでその完成形がこちら。

```rust
pub struct Writer {
    messages: Vec<Message>,
    // 中略
}
impl Writer {
    pub fn new() -> Writer {
        Writer {
            messages: Vec::new(),
            // 中略
        }
    }
    pub fn push(&mut self, message: Message) {
        self.messages.push(message);
    }
    // 中略
    pub fn write(&self, path: &str) -> Result<(), io::Error> {
        let path = path::Path::new(path);
        let mut file = io::BufWriter::new(fs::OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(path)?);
        // 中略：ヘッダー書き込み
        for message in self.messages.iter() {
            match *message {
                Message::TrackChange => {
                    file.write(&Message::TrackChange.binary())?;
                    // 中略：トラックサイズの書き込み
                }
                _ => {
                    // ランニングステータスに関する処理は省略しています
                    file.write(&message.binary())?;
                }
            }
        }
        Ok(file.flush()?)
    }
}
```

`Message`を事前に配列の形で登録しておき、順に書き出しています。`Message`をバイナリ化する面倒な処理は全て`Message`に実装した`fn binary(...)`に分離することで見通しを良くしています。なお、[`std::io::BufWriter`](https://doc.rust-lang.org/std/io/struct.BufWriter.html)でファイルへの書き出しを行う場合、ライフタイムの終わりでエラーが出ても無視されてしまうため、最後に`fn flush(...)`を呼ぶのがお作法となっています。

* κeen氏「[Rustでエラーが出てないのにファイルに書き出せないときは](http://keens.github.io/blog/2017/06/13/rustdeera_gadetenainonifairunikakidasenaitokiha/)」（個人ブログ、2017年６月）

## 貨物検査とハードボイルド・ワンダーランド

時代は便利になったもので、RustではCargoにより３種類のテスト[^11]を書けます。例えば以下はtestsモジュールに記述したテスト。構造体`VLQ`及び`VLQBuilder`を記述しているファイル`src/formats.rs`に一緒に記述しています。

```rust
#[cfg(test)]
mod vlq_tests {
    use formats::*;
    #[test]
    fn binary_98327() {
        let tester = VLQBuilder::new().push(134).push(0b10000000).push(23).build();
        assert_eq!(tester.val(), 98327);
        assert_eq!(tester.len(), 3);
        assert_eq!(tester.binary(), [134, 0b10000000, 23]);
    }
    // 中略：境界値テスト等
}
```
ビット演算を多用するVLQもこれで（そこそこ）安心。また、`tests`ディレクトリを作成してその中に結合テストを書くこともできます。例えば、以下のテストを`tests/test.rs`に記述しています。

```rust
extern crate byteorder;
extern crate ghakuf;

use byteorder::{BigEndian, WriteBytesExt};
use ghakuf::messages::*;
use ghakuf::writer::*;
use std::fs::{OpenOptions, File};
use std::io::prelude::*;
use std::io::Read;

#[test]
fn build_integration_testing() {
    let mut writer = Writer::new();
    let test_messages = test_messages();
    for message in test_messages {
        writer.push(message);
    }
    assert!(writer.write("tests/test_build.mid").is_ok());
    let mut data_write = Vec::new();
    let mut f = File::open("tests/test_build.mid").unwrap();
    f.read_to_end(&mut data_write).unwrap();
    let mut data_read = Vec::new();
    let mut f = File::open("tests/test.mid").unwrap();
    f.read_to_end(&mut data_read).unwrap();
    if data_read.len() == 0 || data_write.len() == 0 {
        assert!(false);
    }
    assert_eq!(data_read, data_write);
}

fn test_messages() -> Vec<Message> {
    let mut test_messages: Vec<Message> = Vec::new();
    let tempo: u32 = 60 * 1000000 / 102; //bpm:102
    test_messages.push(Message::MetaEvent {
        delta_time: 0,
        event: MetaEvent::SetTempo,
        data: [(tempo >> 16) as u8, (tempo >> 8) as u8, tempo as u8].to_vec(),
    });
    test_messages.push(Message::MetaEvent {
        delta_time: 0,
        event: MetaEvent::EndOfTrack,
        data: Vec::new(),
    });
    test_messages.push(Message::TrackChange);
    // 中略
    test_messages
}
```
そして、メソッド等の先頭に記述するドキュメントにもテストを書くことができます。以下のドキュメント、テスト及びコードは、`src/reader.rs`に記述しています。

```rust
/// Parses SMF messages and fires(broadcasts) handlers.
///
/// # Examples
///
/// ```
/// use ghakuf::messages::*;
/// use ghakuf::reader::*;
///
/// let mut reader = Reader::new(
///     Box::new(HogeHandler {}),
///     "tests/test.mid",
/// ).unwrap();
/// let _ = reader.read();
///
/// struct HogeHandler {}
/// impl Handler for HogeHandler {
///     // 中略
/// }
/// ```
pub fn read(&mut self) -> Result<(), ReadError> {
    // 中略
}
```
今回記述したテストは[コードカバレッジ](https://ja.wikipedia.org/wiki/コード網羅率)を考慮していませんが、重要な部分の動作確認はなんとなく大体それなりに行っています。

## 「？」「！」

それでは、ライブラリが完成したので全世界に生き恥をさらしましょう。今回は、バージョン管理を[Git](https://github.com/git/git)で行った上で、コードを[GitHub](https://github.com/)で公開しています。Git・GitHubの概要や使い方は、以下のサイトがわかりやすくてオススメです。

* ay3氏「[GitHub 入門](http://qiita.com/ay3/items/8d758ebde41d256a32dc)」（Qiita記事、2016年９月）

GitHubでの公開時は、レポジトリにREADME.mdを用意して使用例等を書いておくと、トップに表示されて格好良いかもしれません。[ライセンス](https://opensource.org/licenses)を記述したファイルも忘れずにご用意を。

[![github.png](https://qiita-image-store.s3.amazonaws.com/0/193268/feba274e-b4a5-a95a-51ef-7f60bd14df1e.png)](https://github.com/acknak/ghakuf)

また、CargoにはRustのコメントからドキュメントを作成する、Javadoc類似の機能が備わっています。複雑なルールがあるわけではないため、以下のサイトを参考に、簡単に作成してみました。

* Jeremiah Peschka氏「[Writing Documentation in Rust](https://facility9.com/2016/05/writing-documentation-in-rust/)」（個人ブログ、2016年５月）

このドキュメントは、後述するcrates.ioへの登録時に[docs.rs](https://docs.rs/)で公開することができます。

[![doc_rs.png](https://qiita-image-store.s3.amazonaws.com/0/193268/d9a5b390-3ee3-8cb6-a791-cdaa03454e87.png)](https://docs.rs/ghakuf/0.3.2/ghakuf/)

作成したライブラリは、[crates.io](https://crates.io/)に登録すれば、他の人が簡単に使えるようになります。登録方法は以下を参照。

* 「[Publishing on crates.io](http://doc.crates.io/crates-io.html)」（Cargo公式サイト）
* κeen氏「[Rustのパッケージをcrates.ioに登録する](http://keens.github.io/blog/2016/01/31/rustnopakke_jiwocrates_ionitourokusuru/)」（個人ブログ、2016年１月）

[![cargo.png](https://qiita-image-store.s3.amazonaws.com/0/193268/7afa337e-e3f5-a41d-1e81-f33b5e9abd2d.png)](https://crates.io/crates/ghakuf)

## Eppur ci muove!

ところで、GitHubやcrate.ioで「build passing」というバッヂを見かけたことはありませんか？あれは、[継続的インテグレーション](https://aws.amazon.com/jp/devops/continuous-integration/)のテスト結果を表示するものです。今回は、GitHubと連携して、Linux環境とMac環境でのテストを行える[Travis CI](https://travis-ci.org/)と、Window環境でのテストを行える[AppVeyor](https://ci.appveyor.com/)を試しました。

Travis CIは、当該サイト上でGitHubと連携させた上で、レポジトリに`.travis.yaml`を配置することでオンラインテストを行えます。`.travis.yaml`は以下のサイトを参考に設定しました。

* 松島浩道氏「[GitHubと連携できる継続的インテグレーションツール「Travis CI」入門](http://knowledge.sakura.ad.jp/tech/3754/)」（さくらのナレッジ記事、2016年２月）
* nozaq氏「[RustプロジェクトのCI設定 - テスト実行からカバレッジ計測まで](http://qiita.com/nozaq/items/0061d270d0c6bb7715da)」(Qiita記事、2017年３月)

[![travis_ci.png](https://qiita-image-store.s3.amazonaws.com/0/193268/4c64d44c-7986-4084-460f-7e35f1c7821f.png)](https://travis-ci.org/acknak/ghakuf)

AppVeyorも、当該サイト上でGitHubと連携させた上で、レポジトリに`appveyor.yml`を作成することでオンラインテストを行えます。`appveyor.yaml`は以下のサイトを参考に設定しました。

* κeen氏「[travisとappveyorでクロスプラットフォームなCIする話](http://keens.github.io/blog/2015/12/20/travistoappveyerdekurosupurattofo_munacisuruhanashi/)」（個人ブログ、2015年12月）

[![appveyor.png](https://qiita-image-store.s3.amazonaws.com/0/193268/996276ed-81be-73d0-8b1c-efff1b14f80b.png)](https://ci.appveyor.com/project/acknak/ghakuf)

設定ファイルは、上述のサイトのほか、crates.ioで上位に表示されているライブラリのレポジトリに登録されているものも参考にしています。なお。AppVeyorは、セキュリティの設定が変わったのか、VM上でRustコンパイラをダウンロードしようとすると2017年７月下旬からエラーが出るようになりましたが、以下のQ&Aを参考に設定を追加することで回避できました。

* Igor氏、Feodor Fitsner氏「[Curl can't download files through SSL](http://help.appveyor.com/discussions/problems/7115-curl-cant-download-files-through-ssl)」（AppVeyor support center、2017年７月）

# または私は如何にして心配するのを止めてRustを愛するようになったか

年年歳歳花相似、歳歳年年IT不同。プログラミング言語は百代の過客にして、行かふ技術も又旅人也。電子情報工学の進歩には目覚ましいものがありますが、当面はRustとお付き合いしていきたいと思います。最終的には、VSTホストやら歌声合成ライブラリやらを組みたいのですが、何年後になることやら[^12]。ただ、この脳内ブームはしばらく続きそうです。

最後に、短いですが謝辞を。初心者の私が（拙いとはいえ）ライブラリを組めたのは、一重にSMF・Rustの解説記事を公開してくださっている皆さまのおかげです。特に[わいやぎ](https://sites.google.com/site/yyagisite/)氏、[κeen](http://keens.github.io/)氏におかれましては、足を向けて寝られません。願わくば、さらにこの記事がSMF界、Rust界の教師・反面教師として誰かの糧にならんことを。

[^1]: [良い記事を書くためのガイドライン](http://help.qiita.com/ja/articles/qiita-article-guideline)？知らんなぁ……。
[^2]: koher氏「[null安全でない言語は、もはやレガシー言語だ](http://qiita.com/koher/items/e4835bd429b88809ab33)」（Qiita記事、2016年11月）を参照。
[^3]: あと[5000兆円ほしい](https://kimu3.net/20170601/7972)
[^4]: Musical Instrument Digital Interface。電子楽器や電子音源の演奏情報。2017年６月にIECの規格※になったそうですが、さすがに読む気が……。※[IEC 63035:2017](https://webstore.iec.ch/publication/31626)
[^5]: Rustは安定版のリリース（2015年５月）から日が浅く、仕様の破壊と再構築を繰り返していた頃の情報がネット上に散見されるため、公式ガイドブックが最も頼りになります。
[^6]: やめて！石を投げないで！
[^7]: Rustの列挙型（enum）は様々な型のうち一つを選択するもので、他言語ではタグ付き共用体とも呼ばれているようです。
[^8]: XMLパーサーでいうところの[SAX](https://ja.wikipedia.org/wiki/Simple_API_for_XML)。[DOM](https://ja.wikipedia.org/wiki/Document_Object_Model)ではないし[ドム](https://www.google.co.jp/search?q=ドム&tbm=isch)でもない。
[^9]: 特に意味はない
[^10]: Rust公式ガイドブックに[エラーハンドリング](https://rust-lang-ja.github.io/the-rust-programming-language-ja/1.6/book/error-handling.html)が詳細に記述されています。必見。
[^11]: Rust公式ガイドブックに[テスト](https://rust-lang-ja.github.io/the-rust-programming-language-ja/1.6/book/testing.html)が詳細に記述されています。必見。
[^12]: Rust公式ガイドブックには特に記述されていません。再見。