How do I create stock collections for WinRT collection interfaces?
Beyond implementing COM interfaces yourself, the windows crate provides stock collection implementations for common WinRT collection interfaces. Implementing WinRT collection interfaces can be quite challenging, so this should save you a lot of effort in many cases. The implement
feature is required to make use of these stock implementations.
Let's consider a few examples. The WinRT collection interfaces are all defined in the Foundation::Collections
module, so we'll start by adding a dependency on the windows
crate and include the Foundation_Collections
feature:
[dependencies.windows]
version = "0.52"
features = [
"implement",
"Foundation_Collections",
]
Creating a collection is as simple as using the TryFrom
trait on existing Vec
or BTreeMap
, depending on the kind of collection:
WinRT interface | From |
---|---|
IIterable<T> | Vec<T::Default> |
IVectorView<T> | Vec<T::Default> |
IMapView<K, V> | BTreeMap<K::Default, V::Default> |
So if you need a IIterable
implementation of i32
values you can create it as follows:
use windows::{core::*, Foundation::Collections::*}; fn main() -> Result<()> { let collection = IIterable::<i32>::try_from(vec![1, 2, 3])?; for n in collection { println!("{n}"); } Ok(()) }
The resulting collection
will implement all of the specialized IIterable<i32>
methods.
Did you notice the T::Default
in the table above? The challenge is that when the WinRT collection contains nullable types, unlike i32
, then the collection must necessarily support a backing implementation that support expressing this. The Default
associated type just replaces T
with Option<T>
for such nullable, or reference, types.
Let's consider a slightly more contrived example. Here we'll create an IMapView
with strings for keys and interfaces for values. WinRT strings are not nullable but interfaces are. WinRT strings are represented by HSTRING
in the windows
crate and for the interface we'll just use an IStringable
implementation:
#![allow(unused)] fn main() { use windows::Foundation::*; #[implement(IStringable)] struct Value(&'static str); impl IStringable_Impl for Value { fn ToString(&self) -> Result<HSTRING> { Ok(self.0.into()) } } }
We can now create a std
collection as follows:
#![allow(unused)] fn main() { use std::collections::*; let map = BTreeMap::from([ ("hello".into(), Some(Value("HELLO").into())), ("hello".into(), Some(Value("WORLD").into())), ]); }
The Rust compiler naturally infers the exact type: BTreeMap<HSTRING, Option<IStringable>>
.
Finally, we can wrap that BTreeMap
inside a WinRT collection with the TryInto
trait as follows:
#![allow(unused)] fn main() { let map: IMapView<HSTRING, IStringable> = map.try_into()?; for pair in map { println!("{} - {}", pair.Key()?, pair.Value()?.ToString()?); } }