5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

RustAdvent Calendar 2016

Day 8

Windows にて unsafe な呼び出しをくるんで Rust の世界で扱う

Last updated at Posted at 2016-12-08

はじめに

Rust Advent Calendar 12/8 の記事です。

要求された空間や時間内での動作、 デバイスドライバやオペレーティングシステムのような低レベルなコードなど他の言語が苦手とする多数のユースケースを得意とします。

プログラミング言語Rust より

このうたい文句にやられ、9月くらいから Windows API を Rust で呼び出して、パフォーマンス情報を取得するようなコードを書いていました。
ROki1988/perf_client

Windows API の呼び出しについては、すでにあった retep998/winapi-rs を使っています。
で、winapi-rs は unsafe な関数たちです。
このコードたちをどう Rust の世界へ引き込んだらいいんだろう、と考えたりしたので、
そちらについて触れていきます。

Wrapper を書く

結論として、Wrapper 関数を書くことで対応しました。

fn pdh_open_query() -> Result<winapi::PDH_HQUERY, winapi::PDH_STATUS> {
    use std::ptr;
    let mut hquery = winapi::INVALID_HANDLE_VALUE;
    let ret = unsafe { pdh::PdhOpenQueryW(ptr::null(), 0, &mut hquery) };

    if winapi::winerror::SUCCEEDED(ret) {
        Ok(hquery)
    } else {
        Err(ret)
    }
}

上記関数はパフォーマンスカウンターの値を取得する PDH* 関数の1つになります。
pdh::PdhOpenQueryWwinapi-rs にて定義されている関数です。

書いたり消したりしながら、以下の方針が形になってきました。

  • Wrapper 関数内で unsafe を完結させる
    • かつ unsafe の範囲をなるべく小さくする
  • 結果は Result の文脈で包む
    • かつ Wrapper 関数のエラーの型は ERROR_CODE でそろえる

たぶん当たり前のこと書いてますw けどそろえることが大事、としました。
これで Wrapper 関数の呼び出し元では Rust の世界でコードを書いていけます。

impl<'a> Iterator for PdhControllerIterator<'a> {
    type Item = PdhCollectValue;

    fn next(&mut self) -> Option<Self::Item> {
        if self.index == 0 {
            pdh_collect_query_data(self.pdhc.hquery);
        }

        let item = self.pdhc
            .items
            .get(self.index)
            .iter()
            .flat_map(|c| {
                pdh_get_formatted_counter_value(c.hcounter, PDH_FMT_DOUBLE)
                    .map(|v| PdhCollectValue::new(&c.element, v))
            })
            .last();
        self.index += 1;
        item
    }
}

上記コードでは pdh_collect_query_datapdh_get_formatted_counter_value が Wrapper 関数にあたります。
unsafe は一切見当たらず、Rust の世界で関数をつなげながら、ガシガシ書いていけました。

具体的には Drop トレイトを実装してハンドルの開放をしたり、Iterator トレイトを実装して map で値を取り出せるようにしたりしました。
いやぁ楽しい!

おわりに

Wrapper 関数を書くのは少し手間ですが、Rust の世界の呼び出し元では心地よくコードがかけました。
うたい文句にやられてよかったです。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?