Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prefer &T to *const T for Win32 input parameters #1939

Merged
merged 5 commits into from Jul 25, 2022
Merged

Conversation

kennykerr
Copy link
Collaborator

@kennykerr kennykerr commented Jul 22, 2022

A relatively small change to code gen has a big impact on usability. Basically, Win32 pointer parameters are turned into references. Of those, optional parameters are wrapped in Option. I have avoided applying this to void pointers for the time being as they are a little trickier to deal with. The effect in usability is very compelling. For example:

CreateEventA goes from this:

let handle = CreateEventA(core::ptr::null_mut(), true, false, None)?;

To this:

let handle = CreateEventA(None, true, false, None)?;

Direct2D's EndDraw method goes from this:

target.EndDraw(std::ptr::null_mut(), std::ptr::null_mut())?;

To this:

target.EndDraw(None, None)?;

Non-optional pointer parameter are now immutable references, which doesn't really affect calling patterns but makes it a lot less confusing when reading the docs.

Mutable pointers are now mutable references, wrapped in Option as needed.

Unfortunately, it also highlighted a few more metadata bugs:

microsoft/win32metadata#1005
microsoft/win32metadata#1006

Overall, a very nice improvement for working with Win32 API calls.

Fixes #1441
Fixes #816

Copy link
Contributor

@rylev rylev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is fine, but I am a bit worried about if this ends up making some operations unsafe. &T and &mut T are just specific types of pointers so it should be fine to accept them as parameters and convert them to their respective raw pointer types at the FFI boundary. However, this may force the user to convert raw pointer types into Rust reference types which can be tricky to do in safe way.

For example, if I call an API which returns a raw pointer to me, and then I need to call another API that uses that return value as an argument, this change may force me to convert that raw API into a Rust reference. But this might be difficult to do in a way that is safe. &T and &mut T come with many different guarantees that must be upheld for there not to be UB.

For example, &mut T must:

  • not be null
  • be exclusive (i.e., not aliased at all).
  • the referent must be valid for the entire time that the reference is alive for
  • etc.

I didn't see any changes to the examples that looked suspect, but we should keep an eye out for this.

@@ -23,7 +23,8 @@ fn test() -> Result<()> {

let mut random = GUID::zeroed();

BCryptGenRandom(provider, &mut random as *mut _ as _, core::mem::size_of::<GUID>() as _, 0)?;
// TODO: workaround for https://github.com/microsoft/windows-rs/issues/1938
BCryptGenRandom(provider, std::mem::transmute(&mut random), core::mem::size_of::<GUID>() as _, 0)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is &mut random being transmuted into?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pub unsafe fn BCryptGenRandom<'a, P0>(halgorithm: P0, pbbuffer: &mut u8, cbbuffer: u32, dwflags: u32) -> ::windows::core::Result<()>

But #1938 should turn that into a slice - still require some kind of explicit cast tho.

@@ -45,7 +45,7 @@ fn test() -> Result<()> {
assert!(b.GetCount()? == 123);

let c: IPropertyStoreCapabilities = b.cast()?;
c.IsPropertyWritable(std::ptr::null())?;
c.IsPropertyWritable(&PROPERTYKEY::default())?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems different than it was before. Why isn't None being passed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This parameter is not optional.

@kennykerr
Copy link
Collaborator Author

Yep, I looked for issues and didn't come across any (other than void* which I excluded) but we can return to pointers if we run into issues, while keeping the Option improvement.

@kennykerr kennykerr merged commit 9aef1a2 into master Jul 25, 2022
@kennykerr kennykerr deleted the ptr-to-ref branch July 25, 2022 21:01
@LeonarddeR
Copy link

This pr changed IWTSVirtualChannelManager.CreateListener to take a &u8 instead of a *const u8. I was able to get a *const u8 by simply calling as_ptr() on a null terminated string, but obviously that does no longer work. Would it be possible to document cases like this, or should they be excluded as well?

@kennykerr
Copy link
Collaborator Author

It may be a regression - can you open a new issue if you believe that may be the case? Otherwise I may lose track of it.

@LeonarddeR
Copy link

Created microsoft/win32metadata#1185

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Prefer &T to *const T for Win32 input parameters that aren't optional Mapping Option to nullable types
3 participants