RSS リーダー チュートリアル (VS Code を使用した Windows 用 Rust)

前のトピックでは、Windows 用 Rust と windows クレートについて紹介しました。

ここでは、Really Simple Syndication (RSS) フィードからブログ投稿のタイトルをダウンロードする単純なアプリを記述することで、Windows 用 Rust を試してみましょう。

  1. コマンド プロンプト (cmd.exe) を起動し、Rust プロジェクトを保持するフォルダーに cd で移動します。

  2. 次に、Cargo を介して rss_reader という名前の新しい Rust プロジェクトを作成し、プロジェクトの新しく作成されたフォルダーに cd で移動します。

    cargo new rss_reader
    cd rss_reader
    
  3. ここで、再度 Cargo を介して、bindings という名前の新しいサブプロジェクトを作成します。 下のコマンドでわかるように、この新しいプロジェクトはライブラリであり、呼び出したい Windows API にバインドするときに使う手段として機能することになります。 ビルド時に、ライブラリのサブプロジェクト bindings は、"クレート" (Rust のバイナリまたはライブラリを表す用語) に組み込まれます。 後で見るように、rss_reader プロジェクト内からそのクレートを利用することになります。

    cargo new --lib bindings
    

    bindings を入れ子になったクレートにすることは、rss_reader をビルドすると、インポートするすべてのバインドの結果を bindings でキャッシュできるようになることを意味します。

  4. 次に、VS Code で rss_reader プロジェクトを開きます。

    code .
    
  5. まず、bindings ライブラリの作業を行いましょう。

    VS Code のエクスプローラーで、bindings > Cargo.toml ファイルを開きます。

    プロジェクト作成後に VS Code で開いた Cargo.toml ファイル

    Cargo.toml ファイルは、Rust プロジェクトを、そこにあるすべての依存関係を含めて記述しているテキスト ファイルです。

    現時点では、dependencies セクションは空白です。 そのため、ここでそのセクションを編集します ([build-dependencies] セクションも追加します)。そうすると、次のようになります。

    # bindings\Cargo.toml
    ...
    
    [dependencies]
    windows="0.9.1"
    
    [build-dependencies]
    windows="0.9.1"
    

    bindings ライブラリとビルド スクリプトの両方のために、windows クレートに依存関係を追加したところです。 それにより Cargo で、Windows サポートをパッケージとしてダウンロードし、ビルドしてキャッシュすることができます。 バージョン番号は、いくつであるかを問わず最新バージョンに設定します。これは、windows クレートの Web ページで確認できます。

  6. これで、ビルド スクリプト自体を追加できます。そこが、最終的に依存することになるバインドを生成する場所です。 VS Code で、 [bindings] フォルダーを右クリックし、 [新しいファイル] をクリックします。 「build.rs」という名前を入力し、Enter キーを押します。 build.rs を次のように編集します。

    // bindings\build.rs
    fn main() {
        windows::build!(
            Windows::Foundation::Collections::IVector,
            Windows::Foundation::{IAsyncOperationWithProgress, Uri},
    
            Windows::Web::Syndication::{
                ISyndicationText, RetrievalProgress, SyndicationClient, SyndicationFeed, SyndicationItem,
            },
        );
    }
    

    windows::build! マクロは、.winmd ファイルの形式となっているすべての依存関係を解決し、選択された型のバインディングをメタデータから直接生成する処理を行います。 名前空間全体を要求することも "できました" (Windows::Web::Syndication::* を指定)。 しかしここでは、指定した特定の型 (SyndicationClient など) に対してのみバインドを生成するように要求しています。 このようにすると、必要なだけ少なくまたは多くインポートし、必要になることは決してないもののために、コードの生成とコンパイルが行われるのを待機せずにすみます。

    明示的に使用する型に加えて、すべての依存関係も指定します。 たとえば、Uri 型のパラメーターを必要とする SyndicationClient のメソッドを使用します。 そのため build マクロには、そのメソッドを呼び出すことができるように Windows::Foundation::Uri の定義も含めます。 その他の型は、windows クレート自体の一部となっています。 たとえば windows::Result (間もなく使用される) は、windows クレートによって定義されているので、常に使用することができます。 Windows の型の Pascal 形式の名前空間や型名と比較して、windows::Result の小文字の windows に注目してください。

  7. bindings > src > lib.rs ソース コード ファイルを開きます。 前の手順で生成されたバインドを含めるには、lib.rs にある既定のコードを、次と置き換えます。

    // bindings\src\lib.rs
    windows::include_bindings!();
    

    windows::include_bindings! マクロには、前の手順でビルド スクリプトによって生成されたソース コードが含まれています。 これで、追加の API にアクセスする必要がある場合はいつでも、それらをビルド スクリプト (build.rs) 内に列挙するだけで済みます。

  8. ここで、メインの rss_reader プロジェクトを実装しましょう。 まず、プロジェクトのルートにある Cargo.toml ファイルを開き、内側の bindings クレートに次の依存関係を追加し、windows クレートにも依存関係を追加します。

    # Cargo.toml
    ...
    
    [dependencies] 
    bindings = { path = "bindings" }
    windows = "0.9.1"
    
  9. 最後に、rss_reader プロジェクトの src > main.rs ソース コード ファイルを開きます。 そこには、Hello, world! メッセージを出力する簡単なコードがあります。 メッセージで応答します。 このコードを main.rs の先頭に追加します。

    // src\main.rs
    use bindings::{ 
        Windows::Foundation::Uri,
        Windows::Web::Syndication::SyndicationClient,
    };
    
    fn main() {
        println!("Hello, world!");
    }
    

    use 宣言によって、使おうとしている型へのパスが短縮されます。 前に触れた Uri 型があります。

  10. 新しい Uri を作成するには、このコードを main 関数に追加します。

    // src\main.rs
    ...
    
    fn main() -> windows::Result<()> {
        let uri = Uri::CreateUri("https://blogs.windows.com/feed")?;
    
        Ok(())
    }
    

    main 関数の戻り値の型として、windows::Result を使用していることに注意してください。 これによって物事がより容易になります。オペレーティング システム (OS) API からのエラーを処理するのが一般的であるためです。 windows::Result は、エラーの伝達と簡潔なエラー処理を行う助けとなります。

    Uri を作成するコード行の末尾に、疑問符の演算子があることがわかります。 入力量を減らすため、それを行って、Rust のエラー伝達と短絡論理を利用しています。 これは、この簡単な例では、手動のエラー処理を多数行う必要がないことを意味します。 Rust のこの機能の詳細については、「より簡単なエラー処理のための ? 演算子」を参照してください。

  11. この RSS フィードをダウンロードするには、新しい SyndicationClient オブジェクトを作成します。

    // src\main.rs
    ...
    
    fn main() -> windows::Result<()> {
        let uri = Uri::CreateUri("https://blogs.windows.com/feed")?;
        let client = SyndicationClient::new()?;
    
        Ok(())
    }
    

    new 関数は、Rust の既定のコンストラクターに相当するものです。

  12. これで、SyndicationClient オブジェクトを使用してフィードを取得できます。

    // src\main.rs
    ...
    
    fn main() -> windows::Result<()> {
        let uri = Uri::CreateUri("https://blogs.windows.com/feed")?;
        let client = SyndicationClient::new()?;
        let feed = client.RetrieveFeedAsync(uri)?.get()?;
    
        Ok(())
    }
    

    RetrieveFeedAsync は非同期 API であるため、ブロックする get 関数を (上に示すように) 使用できます。 または、C# や C++ で行うのと同様に (結果を協調的に待機するため) async 関数内で await 演算子を使用できます。

  13. これで、結果として得られる項目を単純に反復処理できるので、タイトルだけを出力しましょう。

    // src\main.rs
    ...
    
    fn main() -> windows::Result<()> {
        let uri = Uri::CreateUri("https://blogs.windows.com/feed")?;
        let client = SyndicationClient::new()?;
        let feed = client.RetrieveFeedAsync(uri)?.get()?;
    
        for item in feed.Items()? {
            println!("{}", item.Title()?.Text()?);
        }
    
        Ok(())
    }
    
  14. ここでは、 [実行] > [デバッグなしで実行] をクリックして (または Ctrl + F5 キーを押して)、ビルドと実行が可能であることを確認しましょう。 テキスト エディター内にも [デバッグ][実行] のコマンドが埋め込まれています。 または、コマンド プロンプトから cargo run コマンドを送信できます (最初に cdrss_reader フォルダーに移動します)。このコマンドが、ビルドを行ってから実行します。

    テキスト エディター内に埋め込まれているデバッグと実行のコマンド

    [ターミナル] ペインの下方では、Cargo によって、windows クレートのダウンロードとコンパイルが正常に行われ、結果がキャッシュされて、それを使用して後続のビルドがより短い時間で完了したことを確認できます。 その後サンプルをビルドして実行し、ブログ投稿のタイトルの一覧を表示しています。

    ブログ投稿のタイトルの一覧

Windows のために Rust をプログラミングすることは、これほど簡単です。 ただし内部的には、Rust でコンパイル時に ECMA-335 (共通言語基盤すなわち CLI) に基づく .winmd ファイルを解析すること、および安全性と効率性の両方を考慮に入れて実行時に COM ベースのアプリケーション バイナリ インターフェイス (ABI) を忠実に遵守することの両方が可能なように、ツールの構築に多くの労力が注がれています。

メッセージ ボックスの表示

Windows 用 Rust では、任意の Windows API (過去、現在、将来) を呼び出すことができることは説明しました。 このセクションでは、Windows メッセージ ボックスをユーザーに表示するコードを追加します。

  1. bindings > build.rs ソース コード ファイルを開き、次に示す関数を生成されたバインドに追加します。

    // bindings\build.rs
    fn main() {
        windows::build!(
            ...
            Windows::Win32::UI::WindowsAndMessaging::MessageBoxA,
        );
    }
    
  2. 次に、プロジェクトの src > main.rs ソース コード ファイルを開き、use 宣言を新しい名前空間、またはモジュールに置き換えます。 最後に、MessageBoxA 関数を呼び出すコードを追加します (Windows API の Rust ドキュメントMessageBoxA も参照してください。MESSAGEBOX_STYLE へのリンクが含まれています)。

    // src\main.rs
    use bindings::{ 
        Windows::Foundation::Uri,
        Windows::Web::Syndication::SyndicationClient,
        Windows::Win32::UI::WindowsAndMessaging::*,
    };
    
    fn main() {
        ...
    
        unsafe {
            MessageBoxA(None, "Text", "Caption", MB_OK);
        }
    
        Ok(())
    }
    

    ご覧のように、これらの古い Win32 API を unsafe としてマークしています (「Unsafe blocks (安全でないブロック)」を参照してください)。

今度はビルドして実行すると、Rust により、ブログ投稿のタイトルの一覧の後に Windows メッセージ ボックスが表示されます。