Skip to content

Commit acb70d0

Browse files
committed
Add Builder naming guidelines
1 parent e6715f0 commit acb70d0

File tree

1 file changed

+136
-0
lines changed

1 file changed

+136
-0
lines changed

text/0000-builder-naming.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
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 doesnt 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

Comments
 (0)