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

Use #[non_exhaustive] attribute on structures #774

Conversation

GabrielMajeri
Copy link
Contributor

Description
The #[non_exhaustive] attribute can also be applied on structures. This will make it impossible to explicitly initialize those structures by specifying the field values outside the wgpu-types crate.

Testing
Checked with player and wgpu-rs.

Copy link
Contributor

@monocodus monocodus bot left a comment

Choose a reason for hiding this comment

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

The .monocodus config not found in your repo. Default config is used.
Check config documentation here

@@ -162,46 +162,39 @@ impl<B: hal::Backend> Adapter<B> {

let adapter_limits = raw.physical_device.limits();

let default_limits = wgt::Limits::default();
let mut limits = wgt::Limits::default();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

A disadvantage is that we cannot explicitly initialize the fields of this structure in wgpu-core, since it's treated as an external crate. We have to initialize the default then mutate it.

Comment on lines -1734 to -1736
/// This struct should be partially initalized using the default method, but binding, visibility,
/// and ty should be set.
pub _non_exhaustive: NonExhaustive,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The advantage is that this field no longer has to exist in the public API. And we can enforce the fact that binding, visibility and ty should be set because people are now forced to use the new() method.

Copy link
Member

@kvark kvark left a comment

Choose a reason for hiding this comment

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

Looks nice!
@cwfitzgerald do you recall why we didn't go this path originally?
I think one of the reasons was that we had to keep the MSRV low enough for Debian to be able to build any of our stuff.

@kvark kvark requested a review from cwfitzgerald July 10, 2020 18:27
@lachlansneff
Copy link
Contributor

From what I gathered from conversations in the matrix, it seemed like going this was wasn't idiomatic, because we couldn't use initialization syntax.

@kvark
Copy link
Member

kvark commented Jul 11, 2020

I'm tempted to say let's drop the current hack and just use the attribute like in the PR. We'll lose the ability to have struct initialization syntax (unfortunately!) but we can cover some of that with builders.

@cwfitzgerald
Copy link
Member

cwfitzgerald commented Jul 11, 2020

So our solution to non-exhaustive needs to fulfill 4 main requirements:

  1. Prevent users from writing code that will break if more members are added. This is the only mandatory requirement.
    a. Preventing non-partial construction.
    b. Preventing non-partial matching.
  2. Allow wgpu-core and wgpu-rs to do complete initialization so that if a member is added all uses in wgpu-core result in compiler errors (compiler driven development 😃)
  3. Make structs easy to use for wgpu-rs users.
  4. Make structs easy to use for us.

This is complicated by wgpu not being a single crate, but 3 separate crates. For the purposes of ensuring forwards comparability, rustc sees no difference between wgpu-core and a user crate in terms of "is it us". This requirement really puts us in a bind in terms what to do.

Our current implementation style fulfills 1a, 2, 3, and 4.

Non-exhaustive on its own has issues. First it prevents both partial and exhaustive construction entirely causing people to have to use ugly out of line mutation to set any extension data, which is a very unrustic interface. Second it thinks that wgpu-core is a user, so it also has to follow non-exhaustive rules, causing wgpu-core to also need to add mutation. This fulfills only 1.

The other option is to keep a non-exhaustive member as a private field. This also prevents partial construction due to what I would consider a rust bug (rust-lang/rust#63538). It also thinks of wgpu-core as a user, also fulfilling only 1.

So this situation is kinda terrible all around. Our current solution unfortunately fails mandatory requirement 1b, so it is actually no good.

I propose an extension of the method @kvark mentioned and combine non-exhaustive with the builder pattern. This would fulfill 1, and arguably 3 and 4. It's not ideal, but I frankly can't spin any other way of doing this that correctly prevents issues.

I have a couple ideas for a decl macros that would make the generation of the builder pattern easier. Additionally derive_builder and typed-builder both exist and are worth taking a closer look at, though they would add back our dep on syn.

Once we get a solution for this problem, feel free to keep working on things for #689.

I'm going to close this as I don't think this will work as is.

@GabrielMajeri GabrielMajeri deleted the use-non-exhaustive-attribute branch July 11, 2020 06:29
@kvark
Copy link
Member

kvark commented Jul 11, 2020

As I asked on the chat, @cwfitzgerald this is great analysis. I don't fully understand why we are closing the PR though. Isn't the plan to go with #[non_exhaustive] anyway?

Another wish, if I may, that we be very careful before introducing the auto-generated builder patterns. It might be better to just write them by hand, this would allow builder methods to make more sense. For example, in SampleDescriptor there can be with_filter(min_filter, mag_filter, mip_filter) instead of 3 separate functions.

@cwfitzgerald
Copy link
Member

cwfitzgerald commented Jul 11, 2020

I closed the PR because it will force us to change the initialization code twice, first to this mutation based approach, then to the builder based approach and I would rather just do it all in one fell swoop than have to change back and forth. There's no real urgency to fix 1b right now, 0.6 is still a bit away 😄

Another wish, if I may, that we be very careful before introducing the auto-generated builder patterns. It might be better to just write them by hand, this would allow builder methods to make more sense. For example, in SampleDescriptor there can be with_filter(min_filter, mag_filter, mip_filter) instead of 3 separate functions.

I entirely agree with this design. I am actually very surprised that the existing proc macros don't support that at all, and means that they are both not useful for our purposes. I think I can come up with a simple-enough decl macro solution that both gets us what we want, and means we don't have to hand write all our builders.

@cwfitzgerald
Copy link
Member

So to summarize a discussion @kvark and I had on the matrix, we've come to the conclusion that it just too early in wgpu's lifecycle to be making decisions about stability. We should revisit the decision when we are thinking about moving from 0.X to 1.X. Until then, let's remove all non-exhaustive and the non-exhaustive struct from all the types, and just continue releasing wgpu as a series of breaking changes. @GabrielMajeri if you would be interested in doing that work, that would be very helpful, if not, I can do it.

@GabrielMajeri
Copy link
Contributor Author

No problem, I'll can work on that.

bors bot added a commit that referenced this pull request Jul 12, 2020
787: Remove non-exhaustive markers r=kvark a=GabrielMajeri

Based on the previous [discussion](#774 (comment)), this PR removes the `NonExhaustive` structure and any `#[non_exhaustive]` attribute since it's too early to decide how to ensure forward compatibility.

**Testing**
Checked the `core`, `types` and `player` crates.

Co-authored-by: Gabriel Majeri <gabriel.majeri6@gmail.com>
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.

4 participants