Rust/WinRT coming soon

My rust adventure continues as I have been furiously working on Rust/WinRT for the last five months or so. I am indebted to Ryan Levick for patiently answering all of my questions and also jumping in and getting deeply involved in the project early on. I am also looking forward to opening it up to the community as soon as possible. Even then, it will be early days and much still do. I remember chatting with Martyn Lovell about this a few years ago and we basically agreed that it takes about three years to build a language projection. Naturally, you can get value out of it before then but that’s what you need to keep in mind when you consider completeness.

Still, I’m starting to be able to make API calls with Rust/WinRT and its very satisfying to see this come together. So, I’ll leave you with a sneak peek to give you sense of what calling Windows APIs looks like in Rust. Here’s the venerable Windows.Foundation.Uri class:

use windows::foundation::*;

let uri = Uri::create_uri("https://kennykerr.ca")?;
assert!(uri.domain()? == "kennykerr.ca");
assert!(uri.port()? == 443);
assert!(uri.to_string()? == "https://kennykerr.ca/");

Immediately you’ll notice this looks far more like Rust (if you’re familiar with Rust) than it looks like C++ or C#. Notice the snake_case on module and method names and the ? operator for error propagation. The Uri class has a constructor that’s implemented by a factory method called CreateUri. Since Rust lacks constructors, we simply take that CreateUri method and project it as create_uri to conform to Rust’s naming conventions. The to_string method comes from the IStringable interface that the Uri class implements. Even though Rust doesn’t support type inheritance, Rust/WinRT ensures that you get the same classy type system that WinRT is built on. Under the hood, Rust/WinRT will naturally use QueryInterface to query for the IStringable interface so that it just works. You can also expect the same on-the-metal performance and efficiency as you do from C++/WinRT.

Here’s another example using the Windows.ApplicationModel.DataTransfer namespace to copy some value onto the clipboard:

use windows::application_model::data_transfer::*;

let content = DataPackage::new()?;
content.set_text("Rust/WinRT")?;

Clipboard::set_content(content)?;
Clipboard::flush()?;

Here we’re calling the DataPackage’s default constructor, but of course Rust doesn’t have constructors. The default constructor is thus replaced with the conventional new method.

And finally, here’s an example of using the Windows.UI.Composition API:

use windows::foundation::numerics::*;
use windows::ui::composition::*;
use windows::ui::*;

let compositor = Compositor::new()?;
let visual = compositor.create_sprite_visual()?;
let red = Colors::red()?;
assert!(red == Color { a: 255, r: 255, g: 0, b: 0 });

let brush = compositor.create_color_brush_with_color(red)?;
visual.set_brush(brush)?;

visual.set_offset(Vector3 { x: 1.0, y: 2.0, z: 3.0, })?;
assert!(visual.offset()? == Vector3 { x: 1.0, y: 2.0, z: 3.0 });

Here you can see we’re creating a Compositor. We use the compositor to create a sprite visual with a red brush and then set the visual’s offset. This seems simple, but that’s a testament to the sheer amount of work that’s already gone into Rust/WinRT to make it seem so natural and native to Rust. The Composition API is one of only two type hierarchies in the Windows API and requires special attention to get right in any language projection, let alone a language that lacks traditional inheritance.

My point here is not to claim these are superb APIs. There may well be a better way to do these tasks in Rust. The point is that Rust/WinRT lets you call any Windows API past, present, and future using code generated on the fly directly from the canonical metadata describing the API and right into your Rust package where you can call them as if they were just another Rust module.

I’m looking forward to sharing more about Rust/WinRT.

27 thoughts on “Rust/WinRT coming soon

  1. Alexander

    Awesome news! 😁 Can’t wait to try it in action!

    One question: Do you already have a plan for projecting Windows.UI namespace? I mean the inheritance and authoring UI components. What would be your preferred approach to this?

    Reply
  2. Krzysztof Lesiak

    I’m very much excited about this! Looking forward to trying it in action. 🙂

    Did you run into issues with mapping certain WinRT constructs/classes to Rust? I’m thinking about something the borrow checker would complain about, i.e. needing shared and mutable references at the same time because of the way a WinRT API was originally designed.

    Reply
  3. Sam

    Dear Kenny,

    What is Microsoft’s continuity plan for these Rust endeavors?

    For example, what will Microsoft do if Mozilla were to fail financially and be no longer able to support Rust’s development and maintenance?

    Or for example, what will Microsoft do if Mozilla takes Rust in a direction that does not align with the needs of Microsoft and its users?

    Will Microsoft fork Rust and continue to maintain it without Mozilla’s involvement?

    Ciao,
    Sam

    Reply
    1. Kenny Kerr Post author

      Hi Sam, our goal is to bring the Windows Runtime to more languages to make it easier for developers of different stripes to write apps and components for Windows. We are however only interested in standard languages and compilers. Personally, I joined Microsoft because I thought that C++/CX was a terrible idea and that we needed a standard C++ solution so I created C++/WinRT. The same goes for Rust. Regarding Rust’s future, I can only speculate, but this is promising.

      Reply
  4. 紅樓鍮

    I notice that all WinRT methods return `Result`s. Have you tried to determine at source-code-generation-time if a method may or may not actually fail and model the return type accordingly? `Colors::Red()` returning a `Result` in particular looks bizarre, considering that aggregate-initializing a `Color` in contrast cannot fail.

    Reply
    1. Kenny Kerr Post author

      The only thing that language projections can go on is the metadata describing the various APIs. Unless those APIs say otherwise, the code generation cannot know whether some API will fail. We might know intuitively that `Red` could not possibly fail, but the code generator doesn’t know that. For all it knows, the implementation might decide to return an error if `Red` is called on Tuesdays. I did however recently add a new WinRT attribute that APIs can use to indicate that a certain API cannot fail, but the `Colors` API is too old to benefit from that.

      Of course in this case, you can just create a `Color` struct with A,R,G,B values instead of calling the API. The use of the `Colors` API was just for illustration purposes. 🙂

      Reply
      1. 紅樓鍮

        Yeah. The fact that WinRT and xlang in general made the unfortunate design decision to model its API structure almost solely after C# is too true for a project that have cross-language interfacing as one of its core objectives.

        As more and more language projections are developed, WinRT APIs will have to standardize even more well-known attributes to specify contracts and invariants that a variety of different programming languages are able to utilize. A lot of WinRT “classes”, for example, are actually data records that, if decorated in the metadata, may be projected into languages as some form of pure data type. This would be essential for a Haskell projection, a language that differentiates side-effectful computations from pure ones. C++ and Rust and even C# could project them as bare `struct`s.

      2. Kenny Kerr Post author

        There will always be trade-offs when trying to come up with a single metadata representation to support multiple languages. WinRT’s choice of ECMA-335 is as good as any and works quite well in practice. Language projections are free to map types uniquely so there is a lot of freedom to innovate and optimize for a given language.

      3. serentty

        So does this mean that the Rust projection wouldn’t be able to get rid of the result wrapper on infallible APIs without it being a breaking change? It seems like having nearly every core API return a result would be difficult to use in practice.

      4. Kenny Kerr Post author

        Well in practice I find you end up using many libraries that are fallible and thus end up with some kind of Result function where you can use the ? syntax for short circuiting. This cuts most of the pain out of using such APIs. However, I did add the ability to adorn APIs with metadata indicating their infallibility so we could retroactively add this to existing APIs known to be infallible and thus avoid the unwrapping entirely. That’s something I’m looking in to.

      5. serentty

        That’s great! I did remember the ? syntax after posting this. I had somehow forgotten about it. I was playing around with these bindings today to pass the time while I wait for yours.

        https://github.com/contextfree/winrt-rust

        The only pain point I encountered with error handling was that I couldn’t propagate both std::Option errors and Windows errors using the same result, since they’re incompatible types, so I ended up unwrapping a lot of Options. The other annoying thing was that Rust didn’t know things like that HttpClient implemented IHttpClient so I had to query a lot of interfaces.

        Still, even with these hacky bindings, I found WinRT a lot nicer to use than Win32 from Rust. No more playing with uninitialized memory and worrying about overrunning strings.

    1. Kenny Kerr Post author

      I don’t know about soon, but yes that’s what this would enable. 🙂 The first step is language projection support and that’s what this project is focused on right now.

      Reply
  5. PELock

    Name something more useless. Rust & WinRT hehe :). I don’t know how to break it to you Kenny – nobody uses WinRT. You lost. Microsoft lost on it. Accept it.

    Reply
  6. serentty

    Hey Kenny, I really appreciate what you’re doing here. Using these new APIs from Rust instead of going through painful FFI hoops to call the Win32 ones is a future I look forward to. I know WinRT got off to a really rough start, but all of the recent changes opening it up to much more traditional desktop applications are making it really enticing. It’s high time that there be an alternative to the Windows APIs that we have had since 1985, and now that the walls between old and new are coming down, WinRT is doing a good job at that. The guy above is probably still stuck in 2012. Keep going with the awesome work!

    Reply
  7. Marcus Rahilly

    Hi Kenny, When do you expect to have a set of GitHub samples to download as you had for cppwinrt and would you need fairly complete Visual Studio / Visual Studio Code support for Rust first?

    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