Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

はじめに

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 の世界の呼び出し元では心地よくコードがかけました。
うたい文句にやられてよかったです。

r_ohki
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした