はじめに

RustはC++に比べて以下のような特長があります。そのため、従来はC++を使用していた場面で、Rustを採用する事例が近年増えてきています。

・所有権・借用システムでメモリ安全性を保証
・コンパイル時のチェックでデータ競合やスレッドの安全性を確保
・ゼロコスト抽象化によりC++並みの高性能を実現
・ヘッダーファイル不要でコード管理とメンテナンスが容易
・Cargoによる統一されたビルド・依存管理で開発効率向上
・明快なエラーメッセージで学習・デバッグがしやすい
・モジュールシステムで型情報を効率的に管理し再解析を削減
・セキュリティ重視のシステム開発に適した設計
・Rust AnalyzerによるIDEフレンドリーな設計

では、従来C++で行われてきた、Windowsネイティブアプリケーションの開発をRustで行うと、どのようになるでしょうか。

WindowsネイティブアプリをRustで作る

現時点では、MFCやWindows Forms、WPFのようなWindows専用の大規模なGUIフレームワークはRustにはありません。ただし、icedSlintTauri のようなクロスプラットフォーム対応のGUIライブラリを使ってWindows向けのアプリを作ることはできます。Rustではクロスプラットフォーム開発の需要が高いので、Windows専用の大規模なフレームワークの開発は活発ではありませんが、Windowsのネイティブアプリの開発はできなくはありません。

Win32アプリケーションをRustで作る

Win32アプリケーションをRustで作ることは、MicrosoftがGitHubで提供しているwindowsクレート(*1)を使うと可能です。ただしwindowsはRustからWin32やCOM, WinRTなどのAPIを使用できるようにするもので、MFCのような抽象化能力はありません。
(*1): Rustにおける、プロジェクトやライブラリを管理する単位のこと

基本的な例として、ウィンドウを表示するプログラムを見てみましょう。

use windows::{
    core::*, Win32::Foundation::*, Win32::Graphics::Gdi::ValidateRect,
    Win32::System::LibraryLoader::GetModuleHandleA, Win32::UI::WindowsAndMessaging::*,
};

fn main() -> Result<()> {
    unsafe {
        let instance = GetModuleHandleA(None)?;
        let window_class = s!("window");

        let wc = WNDCLASSA {
            hCursor: LoadCursorW(None, IDC_ARROW)?,
            hInstance: instance.into(),
            lpszClassName: window_class,

            style: CS_HREDRAW | CS_VREDRAW,
            lpfnWndProc: Some(wndproc),
            ..Default::default()
        };

        let atom = RegisterClassA(&wc);
        debug_assert!(atom != 0);

        CreateWindowExA(
            WINDOW_EX_STYLE::default(),
            window_class,
            s!("This is a sample window"),
            WS_OVERLAPPEDWINDOW | WS_VISIBLE,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            None,
            None,
            None,
            None,
        )?;

        let mut message = MSG::default();

        while GetMessageA(&mut message, None, 0, 0).into() {
            DispatchMessageA(&message);
        }

        Ok(())
    }
}

extern "system" fn wndproc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
    unsafe {
        match message {
            WM_PAINT => {
                println!("WM_PAINT");
                _ = ValidateRect(Some(window), None);
                LRESULT(0)
            }
            WM_DESTROY => {
                println!("WM_DESTROY");
                PostQuitMessage(0);
                LRESULT(0)
            }
            _ => DefWindowProcA(window, message, wparam, lparam),
        }
    }
}

このように、C言語で生のWin32 APIを使ってプログラミングするのと同じようなものになります。ですが、Traitによるデフォルト設定や、再代入禁止変数、パターンマッチング式といった、明快でコードをシンプルに保てる記述ができることがわかります。

WinUI3アプリケーションをRustで作る

WinUI3のGUIアプリケーションを作るためのXAMLサポートは、以前のバージョンのwindowsクレートには実験的モジュールとして存在していましたが、最新版の0.59では削除されてしまいました。当面の間、XAMLがサポートされる予定はないため、GUIアプリケーションを作ることはできません。ただし、Windows App SDKの様々な機能を利用したライブラリやコマンドアプリケーションを作ることはできます。

最後に

今回は、WindowsネイティブアプリをRustで作るとどうなるか?を実践してみました。次回は、組み込みシステム向けのプログラムをRustで作るとどうなるかをご紹介したいと思います。