microsoft / windows-rs

Rust for Windows
https://kennykerr.ca/rust-getting-started/
Apache License 2.0
10.13k stars 473 forks source link

Add WinRT `noexcept` support #3070

Closed kennykerr closed 1 month ago

kennykerr commented 1 month ago

A few years ago, I introduced an optimization for https://github.com/microsoft/cppwinrt that allowed methods and properties to be attributed as noexcept to improve call site code generation. While all WinRT methods must return an HRESULT on the ABI, noexcept methods always return S_OK and are thus infallible. This does not change the way C++ code "looks and feels" but C++/WinRT would mark such methods noexcept in the C++ sense which allows the C++ compiler to avoid a bunch of exception handling overhead at call sites, reducing code size.

In Rust, the noexcept can further be used to avoid having to unwrap method calls that are known to be infallible. This also reduces error handling overhead but additionally simplifies implementations and call sites as method return values may be used directly without unwrapping.

The test included with this update illustrates the impact of this change. Consider the following IDL definition of an interface:

namespace Test
{
    interface ITest
    {
        void MethodString(String test);
        void MethodInt32(Int32 test);
        void MethodTest(ITest test);
        String String;
        Int32 Int32;
        ITest Test;

        [noexcept] void MethodStringN(String test);
        [noexcept] void MethodInt32N(Int32 test);
        [noexcept] void MethodTestN(ITest test);
        [noexcept] String StringN;
        [noexcept] Int32 Int32N;
        [noexcept] ITest TestN;
    }
}

In Rust, the first batch of methods all return a Result while the second do not:

#[test]
fn test_except() -> Result<()> {
    let test: ITest = Test::default().into();

    test.MethodString(h!("abc"))?;
    assert_eq!(test.String()?, "abc");

    test.MethodInt32(123)?;
    assert_eq!(test.Int32()?, 123);

    test.MethodTest(&test)?;
    assert_eq!(&test.Test()?, &test);

    Ok(())
}

#[test]
fn test_noexcept() {
    let test: ITest = Test::default().into();

    test.MethodStringN(h!("abc"));
    assert_eq!(test.StringN(), "abc");

    test.MethodInt32N(123);
    assert_eq!(test.Int32N(), 123);

    test.MethodTestN(&test);
    assert_eq!(test.TestN().as_ref(), Some(&test));
}

Fixes: #2991

kennykerr commented 1 month ago

https://github.com/microsoft/windows-rs/pull/3070/commits/7dfe20bae4210df4577e82df731d48d92a41741f adds interop testing against C++/WinRT.