Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
3
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

@r_ohki

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

はじめに

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

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
3
Help us understand the problem. What are the problem?