contextfree / winrt-rust

Use and (eventually) make Windows Runtime APIs with Rust
Apache License 2.0
142 stars 10 forks source link

Figure out lifetimes for generic parameters #10

Closed Boddlnagg closed 7 years ago

Boddlnagg commented 7 years ago

Currently lifetimes of generic parameters are set to 'static in aliases and return types. This requires the "hack" that for interface and class definitions RtInterface::In is set to *mut interface.If possible, *mut pointers should be avoided (the wrappers are currently defined as unsafe, but that could be changed as soon as we are more confident, and *mut pointers probably shouldn't be part of safe signatures). The problem is that generic types can either be value types or references, but only references have a lifetime. Ideally that lifetime should be tied to the lifetime of a given reference to the outermost type.

As a example, let's take DeviceInformationCollection. It is currently defined as a newtype wrapper around IVectorView<&'static DeviceInformation>. Even if it was defined with a lifetime parameter 'a wrapping IVectorView<&'a DeviceInformation> instead, that would not be enough, because we would need any reference of type &'x DeviceInformationCollection to refer to &'x IVectorView<&'x DeviceInformation>, i.e. the lifetime of the reference itself should be the same as the lifetimes of the generic parameters, with the desired result that whenever the generic parameter T appears in methods of IVectorView<T> and is instantiated with a reference type, its lifetime is always linked to the lifetime of the &mut self reference.

In other words, when a generic interface has a method foo(&mut self, data: T::In) and T is a reference to an interface type ISomething, we want the method to act as if it was defined as foo(&mut self, data: &ISomething) which (due to lifetime elision) is equivalent to foo(&'a mut self, data: &'a ISomething).

But since T can never refer to the 'a of &'a mut self, we need to find a different solution. We also can't trivially do something like foo(&mut self, data: &T::In), because T (and therefore also T::In) might be a value type that needs to be passed by value.

Maybe one could add some lifetime bounds on T or T::In, e.g. foo<'a>(&'a mut self, data: T::In) where T: 'a, I have not tested this yet.

Boddlnagg commented 7 years ago

This is mostly solved in https://github.com/contextfree/winrt-rust/pull/3/commits/3e34a34fc23e4289be3796955659f83b3f0b359e by using &T::In. When T is a value type, the value is copied within unwrap() (the trait RtValueType now implies Copy).

The only remaining lifetime is for strings: since the input type HStringRef<'a> requires a lifetime we still use &'a str as generic parameter. I'm leaving this issue open to find a better solution in that case.

Boddlnagg commented 7 years ago

The last remaining lifetime has been removed in https://github.com/contextfree/winrt-rust/pull/11/commits/d79dc38f6ffa15ef5b18dab3475625326174f32e.