Wgpu-rs: Proposal: Introduce builder pattern

Created on 23 Jul 2020  路  6Comments  路  Source: gfx-rs/wgpu-rs

This proposal argues for introduction of the builder pattern for most of the structs of the public API. It is a reaction to the recent introduction of the usage of Cow in the API which results in quite a lot of noise.

Consider the following example:

let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
  bind_group_layouts: Borrowed(&[&camera_bind_group_layout, &per_molecule_bind_group_layout]),
  push_constant_ranges: Borrowed(&[]),
});

versus

let pipeline_layout = device.create_pipeline_layout(PipelineLayoutDescriptor::new()
    .bind_group_layouts(&[&camera_bind_group_layout, &per_molecule_bind_group_layout])
); 

Notice several things:

  1. With builder pattern there can be some reasonable defaults, like in the case of push_constant_ranges. In contrast to just introducing default implementation everywhere, builder pattern doesn't introduce noise with ... Default::default().
  2. All of the Borrowed can be omitted. This is taken from article https://jwilm.io/blog/from-str-to-cow/ and would be done by annotating setter functions with Input: Into<Cow<ActualInput>.
  3. We can get rid of & at the beginning by implementing Deref (see how Ash does It) and also { } parantheses.

All of this creates, in my opinion: cleaner, more readable, easily writable(especially Borrowed could be confusing for beginners to Rust) WebGPU code.

This builder pattern is inspired by the Ash crate.

enhancement

Most helpful comment

We've also been hinted that we could use our own trait bound instead of Into<Cow<>. The benefit of that would be the ability to manually implement it for &[T; 1], &[T; 2], etc, meaning that the regular slice syntax is used on the user side, and they don't really have to know about the trait.

All 6 comments

Thank you for filing this!
Benefits:

  • wgpu-types struct no longer need to be generic (since wgpu-rs API would no longer try to reuse them)
  • good ability to add fields later without breaking the API
  • better ergonomics

Downsides: more code

Note that that setter API is not actually going to work: If you accept a Input: Into<Cow<'a, [T]>, then passing an &[T; N] argument will not do a deref coercion. That is in fact why we used Borrowed() there: If you tried to use From or Into, you would have to explicitly slice the array literal with [..] as well.

See here for an example.

Something that might be interesting to note is that even though &[T; N] does not coerce to Cow<'a, [T]>, Vec<T> does coerce to Cow<'a, [T]>, therefore the vec! macro could be used. An example of this is shown in the following example.

The only difference of using Vec instead of an array slices is that a Cow::Owned<'a, [T]> is passed instead of Cow::Borrow<'a, [T]>.

I don't think this approach will have any runtime penalty because the Vec is moved but I am not sure about it.

We've also been hinted that we could use our own trait bound instead of Into<Cow<>. The benefit of that would be the ability to manually implement it for &[T; 1], &[T; 2], etc, meaning that the regular slice syntax is used on the user side, and they don't really have to know about the trait.

We've also been hinted that we could use our own trait bound instead of Into<Cow<>. The benefit of that would be the ability to manually implement it for &[T; 1], &[T; 2], etc, meaning that the regular slice syntax is used on the user side, and they don't really have to know about the trait.

I quite like this solution. And when const generics will come, we can just implement it for &[T; S] right?

Closed in https://github.com/gfx-rs/wgpu/pull/840 and then removed.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JoshuaBatty picture JoshuaBatty  路  5Comments

branpk picture branpk  路  3Comments

dmilford picture dmilford  路  3Comments

OptimisticPeach picture OptimisticPeach  路  3Comments

JonathanWoollett-Light picture JonathanWoollett-Light  路  4Comments