|
| 1 | +- Start Date: 2015-04-06 |
| 2 | +- RFC PR: (leave this empty) |
| 3 | +- Rust Issue: (leave this empty) |
| 4 | + |
| 5 | +# Summary |
| 6 | + |
| 7 | +Provide guidelines for builder types in the standard library. |
| 8 | + |
| 9 | +# Motivation |
| 10 | + |
| 11 | +We use builder pattern in a few places in the standard library, but they are not consistent. We |
| 12 | +have `thread::Builder`, `process::Command` and there’s `fs::OpenOptions`. They are somewhat ad-hoc |
| 13 | +and inconsistency makes forming the intuition about them hard. |
| 14 | + |
| 15 | +A common example is people not knowing about `OpenOptions` builder and being confused by its name |
| 16 | +and lack of discoverability. |
| 17 | + |
| 18 | +# Detailed design |
| 19 | + |
| 20 | +## Guidelines |
| 21 | + |
| 22 | +### Naming |
| 23 | + |
| 24 | +These guidelines suggest a few naming schemes, depending on situation at hand: |
| 25 | + |
| 26 | +* If the module, which exports the builder, is expected to only export a single builder, even in |
| 27 | + the future, and the module itself has a descriptive name, calling the builder “Builder” is |
| 28 | + recommended. For example: |
| 29 | + |
| 30 | + * `thread::Builder` builds and spawns threads; |
| 31 | + * `process::Builder` builds and spawns processes; |
| 32 | + |
| 33 | + but *not* `fs::Builder`, which opens files; |
| 34 | +* If the builder produces one type of objects, use the name of buildee suffixed with “Builder”. |
| 35 | + This scheme has an advantage of being searchable and self-documenting. For example: |
| 36 | + `FileBuilder` is a builder and produces objects of type `File`; |
| 37 | +* If there is no clear buildee (most likely because it contains multiple finalizers), a noun |
| 38 | + describing all the buildable objects suffixed with “Builder” may be used. For example: |
| 39 | + `ThreadBuilder`, even though it produces `JoinHandle`s and `JoinGuard`s rather than `Thread`s; |
| 40 | +* Finally, if the majority (community) decides there exists a superior name for the builder, but it |
| 41 | + does not match any the guidelines above, generously point out the existence of the builder in |
| 42 | + the documentation of buildee and module, if appropriate. |
| 43 | + |
| 44 | +### Methods |
| 45 | + |
| 46 | +Option setters – methods which represent options that can be set on the builder – should receive |
| 47 | +the builder object as `&mut self` rather than `self` unless there is a good reason to do otherwise. |
| 48 | + |
| 49 | +Finalizers – methods which produce the built object – should receive self via reference, unless |
| 50 | +there is a good reason to do otherwise. |
| 51 | + |
| 52 | +In order for method listing in documentation to be predictable, methods in the builder’s `impl` |
| 53 | +block should be declared following this order: |
| 54 | + |
| 55 | +* `new` – a method to create the builder object comes first in the `impl` block; |
| 56 | +* followed by one or more option setters; |
| 57 | +* followed by one or more finalizers. |
| 58 | + |
| 59 | +This ordering stems from conventional use of the builder pattern: create a builder, set the |
| 60 | +options and call a finalizer. |
| 61 | + |
| 62 | +#### Method naming |
| 63 | + |
| 64 | +Option setters should be nouns or adjectives. Avoid `is_*` prefix and nouns derived from |
| 65 | +adjectives. For example: |
| 66 | + |
| 67 | +* `name`; |
| 68 | +* `stack_size`; |
| 69 | +* `read` or `readable` (rather than `is_readable` or `readability`); |
| 70 | +* `write` or `writable` (rather than `is_writable` or `writability`). |
| 71 | + |
| 72 | +Finalizers should be verbs. For example: |
| 73 | + |
| 74 | +* `spawn`; |
| 75 | +* `create`; |
| 76 | +* `open`. |
| 77 | + |
| 78 | +## Proposed changes to the standard library |
| 79 | + |
| 80 | +### `thread::Builder` |
| 81 | + |
| 82 | +`thread::Builder` is adjusted to have the following interface: |
| 83 | + |
| 84 | +```rust |
| 85 | +pub struct Builder { … } |
| 86 | +impl Builder { |
| 87 | + fn new() → Builder; |
| 88 | + fn name(&mut self, name: String) → &mut Builder; |
| 89 | + fn stack_size(&mut self, size: usize) → &mut Builder; |
| 90 | + fn spawn<F>(&mut self, f: F) → Result<JoinHandle> where F: FnOnce(), F: Send + 'static; |
| 91 | + fn scoped<'a, T, F>(&mut self, f: F) → Result<JoinGuard<'a, T>> |
| 92 | + where T: Send + 'a, F: FnOnce() -> T, F: Send + 'a |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +and `impl Clone for Builder` is provided. |
| 97 | + |
| 98 | +In summary, all methods are changed to take and return `Builder` by mutable reference rather than |
| 99 | +by value. Strictly speaking this is a breaking change, but most users of the `thread::Builder` |
| 100 | +should not encounter any breakage. |
| 101 | + |
| 102 | +### `process::Command` |
| 103 | + |
| 104 | +`impl Clone for Command` is provided. |
| 105 | + |
| 106 | +### `fs::OpenOptions` |
| 107 | + |
| 108 | +`OpenOptions` is renamed to `FileBuilder`. `OpenOptions` shall stay as a deprecated alias for |
| 109 | +`FileBuilder` for a period. |
| 110 | + |
| 111 | +# Drawbacks |
| 112 | + |
| 113 | +Breaking change after 1.0-beta. |
| 114 | + |
| 115 | +# Alternatives |
| 116 | + |
| 117 | +* Only apply these guidelines to builders introduced after 1.0-beta; |
| 118 | +* Construct builders via a method implemented on the buildees. The API looks like this: |
| 119 | + |
| 120 | + let file = File::builder() |
| 121 | + .write(true) |
| 122 | + .truncate(false) |
| 123 | + .open(path); |
| 124 | + |
| 125 | + This allows users of the API never reference the builder type. On the other hand, it is only |
| 126 | + suitable for a subset of builders. For example it doesn’t work for `thread::Builder`, because |
| 127 | + the finalisers return guards rather than an instance of `Thread`; |
| 128 | + |
| 129 | + Proposed by [@sfackler][sfackler]. |
| 130 | +* Rename `process::Command` to `process::Builder`. |
| 131 | + |
| 132 | +[sfackler]: https://github.com/rust-lang/rfcs/pull/1044#discussion_r28082235 |
| 133 | + |
| 134 | +# Unresolved questions |
| 135 | + |
| 136 | +None known to the author. |
0 commit comments