Getting started with Rust/WinRT

Getting started with Rust/WinRT is quite simple thanks in large part to the polished toolchain that Rust developers enjoy. Here are a few links if you don’t need any help getting started. Read on if you’d like to learn some tips and tricks to make the most out of the Windows Runtime.

GitHub: https://github.com/microsoft/winrt-rs
Docs.rs: https://docs.rs/winrt/
Crates.io: https://crates.io/crates/winrt

Install the following prerequisites:

Visual Studio 2019 – be sure to install the C++ tools as this is required by the Rust compiler (only the linker is required).
Visual Studio Code – this is the default IDE used for Rust.
Python – be sure to install the x64 version as this is required for debugging support.
Git – Rust has deep support for Git.
Rust – this installs `rustup` which is a tool for installing Rust toolchains and common Rust related tooling.

Now open VS Code and type `Ctrl+Shift+X` to open the extensions panel and install the following extensions:

rust-analyzer – there are others, but this is the only Rust extension that I’ve tried that actually works reliably most of the time.
CodeLLDB – you can also use the Microsoft C++ extension for debugging, but this one does a better job of integrating with the IDE.
C/C++ – the Microsoft C++ extension doesn’t integrate as well with the IDE, but provides superior debugging information, so you may want to have that on hand for an emergency.

You should be prompted to download and install the Rust language server. Go ahead and let that install. You may need to restart VS Code and give it a few moments to load, after which it should all be ready and working pretty well.

Let’s now start real simple with a new cargo package:

C:\>cargo new sample
     Created binary (application) `sample` package

Cargo is Rust’s package manager. This command will create a minimal project that you can open with VS Code:

C:\>cd sample
C:\sample>code .

Open the Cargo.toml file that cargo created for the project and add the WinRT crate as a dependency:

[dependencies]
winrt = "0.7.0"

That’s the current version as I write this, but you can check Crates.io for the latest version. You can use cargo once again to build the application:

C:\sample>cargo build
    Updating crates.io index
   Compiling proc-macro2 v1.0.18
   Compiling unicode-xid v0.2.0
   Compiling syn v1.0.30
   Compiling ryu v1.0.5
   Compiling serde v1.0.111
   Compiling itoa v0.4.5
   Compiling sha1 v0.6.0
   Compiling quote v1.0.6
   Compiling serde_json v1.0.53
   Compiling winrt_gen_macros v0.7.0
   Compiling winrt_gen v0.7.0
   Compiling winrt_macros v0.7.0
   Compiling winrt v0.7.0
   Compiling sample v0.1.0 (C:\sample)
    Finished dev [unoptimized + debuginfo] target(s) in 19.65s

The first time you run cargo, it goes ahead and downloads any dependencies recursively. This might seem like a lot, but it is not unusual for Rust crates to depend on a variety of other crates. The good news is that cargo will cache the compiled crates and reuse those results, ensuring subsequent builds are very snappy. You can also have cargo run the application directly, which will rebuild the application if necessary:

C:\sample>cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.06s
     Running `target\debug\sample.exe`
Hello, world!

Notice how the initial build took 20 seconds, while the subsequent run hardly took any time at all. Of course, nothing had changes so let’s change that. In the project’s src folder you’ll find a main.rs source file. There you can see the source of the Hello world greeting. Let’s now use the winrt::import macro to generate Rust bindings for WinRT APIs.

winrt::import!(
    dependencies
        os
    types
        windows::data::xml::dom::*
);

It doesn’t really matter where in main.rs you put this code, but I usually put it at the top as it logically includes a bunch of Rust code that you can then use in your application. The import macro has two parts. There are the dependencies that identify the WinRT components you wish to make use of in your application and the specific subset of types within those dependencies that you actually want to use. In this case, I’ve used “os” to make use of all available operating system APIs. Those correspond to the version of Windows that you happen to be running on. It’s also possible to target a specific Windows SDK version. I’ve also constrained the import macro to just those types in the windows::data::xml::dom module. This corresponds to the Windows.Data.Xml.Dom namespace in the official documentation. As you might have guessed, this is a Rust path and you can certainly constrain it further to include only specific types within different modules if you wish.

Let’s now replace the main function provided by cargo with something a little more interesting. Here I’m using the XmlDocument struct generated by the import macro, which is documented here.

fn main() -> winrt::Result<()> {
    use windows::data::xml::dom::*;

    let doc = XmlDocument::new()?;
    doc.load_xml("<html>hello world</html>")?;

    let root = doc.document_element()?;
    assert!(root.node_name()? == "html");
    assert!(root.inner_text()? == "hello world");

    Ok(())
}

If you were to recompile at this point, you may notice it taking just a little while:

C:\sample>cargo run
   Compiling sample v0.1.0 (C:\sample)
    Finished dev [unoptimized + debuginfo] target(s) in 8.71s
     Running `target\debug\sample.exe`

8 seconds isn’t so bad, but as you add more types the import macro will naturally have more work to do and the Rust compiler will spend more time processing the results. The time it takes can quickly become prohibitive and a more scalable solution is needed. Still, the import macro is handy for getting started or just quickly calling a specific Windows API. Another option is to use a Rust build script to generate and cache the results of importing WinRT types.

We’ll cover that next time, so stay tuned!

5 thoughts on “Getting started with Rust/WinRT

  1. Mike

    Since visual studio requires purchasing a subscription in all but small commercial setting, Is it possible to compile rust against WinRT without having to install Visual Studio 2019 as a prerequisite ?

    Reply
  2. Marcus Rahilly

    I installed the C++ Build Tools to lighten the load on an old desktop. C++ code built fine but rustc would not compile until I downloaded Visual Studio Community 2019.

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s