Skip to content

Commit 6507877

Browse files
bors[bot]matklad
andauthored
Merge #6437
6437: Document doer object anti-pattern r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
2 parents e7f9086 + 412d6b3 commit 6507877

File tree

1 file changed

+54
-0
lines changed

1 file changed

+54
-0
lines changed

docs/dev/style.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,60 @@ impl Foo {
211211

212212
Prefer `Default` even it has to be implemented manually.
213213

214+
## Functions Over Objects
215+
216+
Avoid creating "doer" objects.
217+
That is, objects which are created only to execute a single action.
218+
219+
```rust
220+
// Good
221+
do_thing(arg1, arg2);
222+
223+
// Not as good
224+
ThingDoer::new(arg1, arg2).do();
225+
```
226+
227+
Note that this concerns only outward API.
228+
When implementing `do_thing`, it might be very useful to create a context object.
229+
230+
```rust
231+
pub fn do_thing(arg1: Arg1, arg2: Arg2) -> Res {
232+
let mut ctx = Ctx { arg1, arg2 }
233+
ctx.run()
234+
}
235+
236+
struct Ctx {
237+
arg1: Arg1, arg2: Arg2
238+
}
239+
240+
impl Ctx {
241+
fn run(self) -> Res {
242+
...
243+
}
244+
}
245+
```
246+
247+
The difference is that `Ctx` is an impl detail here.
248+
249+
Sometimes a middle ground is acceptable if this can save some busywork:
250+
251+
```rust
252+
ThingDoer::do(arg1, arg2);
253+
254+
pub struct ThingDoer {
255+
arg1: Arg1, arg2: Arg2,
256+
}
257+
258+
impl ThingDoer {
259+
pub fn do(arg1: Arg1, arg2: Arg2) -> Res {
260+
ThingDoer { arg1, arg2 }.run()
261+
}
262+
fn run(self) -> Res {
263+
...
264+
}
265+
}
266+
```
267+
214268
## Avoid Monomorphization
215269

216270
Rust uses monomorphization to compile generic code, meaning that for each instantiation of a generic functions with concrete types, the function is compiled afresh, *per crate*.

0 commit comments

Comments
 (0)