Skip to content

Commit 656c597

Browse files
authored
Merge branch 'master' into state_pattern
2 parents 2ce75c4 + c9c5f70 commit 656c597

File tree

3 files changed

+76
-4
lines changed

3 files changed

+76
-4
lines changed

SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
- [Type Consolidation into Wrappers](./patterns/ffi/wrappers.md)
4040

4141
- [Anti-patterns](./anti_patterns/index.md)
42+
- [Clone to satisfy the borrow checker](./anti_patterns/borrow_clone.md)
4243
- [`#[deny(warnings)]`](./anti_patterns/deny-warnings.md)
4344
- [Deref Polymorphism](./anti_patterns/deref.md)
4445

anti_patterns/borrow_clone.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Clone to satisfy the borrow checker
2+
3+
## Description
4+
5+
The borrow checker prevents Rust users from developing otherwise unsafe code by
6+
ensuring that either: only one mutable reference exists, or potentially many but
7+
all immutable references exist. If the code written does not hold true to these
8+
conditions, this anti-pattern arises when the developer resolves the compiler
9+
error by cloning the variable.
10+
11+
## Example
12+
13+
```rust
14+
// define any variable
15+
let mut x = 5;
16+
17+
// Borrow `x` -- but clone it first
18+
let y = &mut (x.clone());
19+
20+
// perform some action on the borrow to prevent rust from optimizing this
21+
//out of existence
22+
*y += 1;
23+
24+
// without the x.clone() two lines prior, this line would fail on compile as
25+
// x has been borrowed
26+
// thanks to x.clone(), x was never borrowed, and this line will run.
27+
println!("{}", x);
28+
```
29+
30+
## Motivation
31+
32+
It is tempting, particularly for beginners, to use this pattern to resolve
33+
confusing issues with the borrow checker. However, there are serious
34+
consequences. Using `.clone()` causes a copy of the data to be made. Any changes
35+
between the two are not synchronized -- as if two completely separate variables
36+
exist.
37+
38+
There are special cases -- `Rc<T>` is designed to handle clones intelligently.
39+
It internally manages exactly one copy of the data, and cloning it will only
40+
clone the reference.
41+
42+
There is also `Arc<T>` which provides shared ownership of a value of type T
43+
that is allocated in the heap. Invoking `.clone()` on `Arc` produces a new `Arc`
44+
instance, which points to the same allocation on the heap as the source `Arc`,
45+
while increasing a reference count.
46+
47+
In general, clones should be deliberate, with full understanding of the
48+
consequences. If a clone is used to make a borrow checker error disappear,
49+
that's a good indication this anti-pattern may be in use.
50+
51+
Even though `.clone()` is an indication of a bad pattern, sometimes
52+
**it is fine to write inefficient code**, in cases such as when:
53+
54+
- the developer is still new to ownership
55+
- the code doesn't have great speed or memory constraints
56+
(like hackathon projects or prototypes)
57+
- satisfying the borrow checker is really complicated and you prefer to
58+
optimize readability over performance
59+
60+
If an unnecessary clone is suspected, The [Rust Book's chapter on Ownership](https://doc.rust-lang.org/book/ownership.html)
61+
should be understood fully before assessing whether the clone is required or not.
62+
63+
Also be sure to always run `cargo clippy` in your project, which will detect some
64+
cases in which `.clone()` is not necessary, like [1](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone),
65+
[2](https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy),
66+
[3](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone) or [4](https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref).
67+
68+
## See also
69+
70+
- [`mem::{take(_), replace(_)}` to keep owned values in changed enums](../idioms/mem-replace.md)
71+
- [`Rc<T>` documentation, which handles .clone() intelligently](http://doc.rust-lang.org/std/rc/)
72+
- [`Arc<T>` documentation, a thread-safe reference-counting pointer](https://doc.rust-lang.org/std/sync/struct.Arc.html)
73+
- [Tricks with ownership in Rust](https://web.archive.org/web/20210120233744/https://xion.io/post/code/rust-borrowchk-tricks.html)

idioms/mem-replace.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,5 @@ like Indiana Jones, replacing the artifact with a bag of sand.
116116

117117
## See also
118118

119-
This gets rid of the [Clone to satisfy the borrow checker] antipattern in a
120-
specific case.
121-
122-
[Clone to satisfy the borrow checker](TODO: Hinges on PR #23)
119+
This gets rid of the [Clone to satisfy the borrow checker](../anti_patterns/borrow_clone.md)
120+
antipattern in a specific case.

0 commit comments

Comments
 (0)