summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/src/docs/mutable_key_type.txt
blob: 15fe34f2bb5ec5ba6602802b5f16ce13ace34b17 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
### What it does
Checks for sets/maps with mutable key types.

### Why is this bad?
All of `HashMap`, `HashSet`, `BTreeMap` and
`BtreeSet` rely on either the hash or the order of keys be unchanging,
so having types with interior mutability is a bad idea.

### Known problems

#### False Positives
It's correct to use a struct that contains interior mutability as a key, when its
implementation of `Hash` or `Ord` doesn't access any of the interior mutable types.
However, this lint is unable to recognize this, so it will often cause false positives in
theses cases.  The `bytes` crate is a great example of this.

#### False Negatives
For custom `struct`s/`enum`s, this lint is unable to check for interior mutability behind
indirection.  For example, `struct BadKey<'a>(&'a Cell<usize>)` will be seen as immutable
and cause a false negative if its implementation of `Hash`/`Ord` accesses the `Cell`.

This lint does check a few cases for indirection.  Firstly, using some standard library
types (`Option`, `Result`, `Box`, `Rc`, `Arc`, `Vec`, `VecDeque`, `BTreeMap` and
`BTreeSet`) directly as keys (e.g. in `HashMap<Box<Cell<usize>>, ()>`) **will** trigger the
lint, because the impls of `Hash`/`Ord` for these types directly call `Hash`/`Ord` on their
contained type.

Secondly, the implementations of `Hash` and `Ord` for raw pointers (`*const T` or `*mut T`)
apply only to the **address** of the contained value.  Therefore, interior mutability
behind raw pointers (e.g. in `HashSet<*mut Cell<usize>>`) can't impact the value of `Hash`
or `Ord`, and therefore will not trigger this link.  For more info, see issue
[#6745](https://github.com/rust-lang/rust-clippy/issues/6745).

### Example
```
use std::cmp::{PartialEq, Eq};
use std::collections::HashSet;
use std::hash::{Hash, Hasher};
use std::sync::atomic::AtomicUsize;

struct Bad(AtomicUsize);
impl PartialEq for Bad {
    fn eq(&self, rhs: &Self) -> bool {
         ..
; unimplemented!();
    }
}

impl Eq for Bad {}

impl Hash for Bad {
    fn hash<H: Hasher>(&self, h: &mut H) {
        ..
; unimplemented!();
    }
}

fn main() {
    let _: HashSet<Bad> = HashSet::new();
}
```