# Using std::collections and unsafe: anything can happen

**URL:** <https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640>\
**Category:** documentation\
**Created:** [May 17, 2022, 9:35am UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640 "2022-05-17T09:35:34Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![jbe](https://avatars.discourse-cdn.com/v4/letter/j/f6c823/32.png) [@jbe](https://internals.rust-lang.org/u/jbe)\
**Post date:** [May 17, 2022, 9:35am UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/1 "2022-05-17T09:35:34Z")

</div>

Continuing the discussion from [Avoiding unsafe when using a C library: LMDB](https://users.rust-lang.org/t/avoiding-unsafe-when-using-a-c-library-lmdb/75702) on URLO:

I recently got irritated by the documentation of `HashSet` / `HashMap` and the like, and the Rust reference regarding "logic errors", and I see a fundamental problem in the documentation regarding "logic errors" and (un)safety.

The documentation of [`HashSet`](https://doc.rust-lang.org/std/collections/hash_set/struct.HashSet.html) says:

> It is a logic error for an item to be modified in such a way that the item’s hash, as determined by the [`Hash`](https://doc.rust-lang.org/std/hash/trait.Hash.html) trait, or its equality, as determined by the [`Eq`](https://doc.rust-lang.org/std/cmp/trait.Eq.html) trait, changes while it is in the set. This is normally only possible through [`Cell`](https://doc.rust-lang.org/std/cell/struct.Cell.html), [`RefCell`](https://doc.rust-lang.org/std/cell/struct.RefCell.html), global state, I/O, or unsafe code. The **behavior** resulting from such a logic error is **not specified** (it could include panics, incorrect results, aborts, memory leaks, or non-termination) but **will not be undefined behavior**.

The Rust reference says:

> [Logic errors](https://doc.rust-lang.org/reference/behavior-not-considered-unsafe.html?highlight=logic%20error#logic-errors)
> 
> Safe code may impose extra logical constraints that can be checked at neither compile-time nor runtime. If a program breaks such a constraint, the behavior may be unspecified but will not result in undefined behavior. This could include panics, incorrect results, aborts, and non-termination. The behavior may also differ between runs, builds, or kinds of build.
> 
> For example, implementing both `Hash` and `Eq` requires that values considered equal have equal hashes. Another example are data structures like `BinaryHeap`, `BTreeMap`, `BTreeSet`, `HashMap` and `HashSet` which describe constraints on the modification of their keys while they are in the data structure. Violating such constraints is not considered unsafe, yet the program is considered erroneous and **its behavior unpredictable**.

Now I'm not entirely sure how to interpret this, but I would interpret it as follows: Anything can happen in these cases, except those things that require `unsafe` to happen.

Undefined behavior means _anything_ can happen, but the "not specified" behavior basically means anything can happen except the things that can make anything happen (i.e. a proper/strict subset of all possible behavior).

While I understand the reason to make a difference here, this still makes reasoning about a program practically impossible, and in particular it makes it impossible to soundly use `unsafe`. Consider the following example:

```rust
mod i_am_not_unsafe {
    
    use rand::random;
    use std::collections::HashSet;
    use std::hash::{Hash, Hasher};
    
    struct Wobbly;
    
    impl PartialEq for Wobbly {
        fn eq(&self, _: &Wobbly) -> bool {
            random()
        }
    }
    
    impl Eq for Wobbly {}
    
    impl Hash for Wobbly {
        fn hash<H: Hasher>(&self, state: &mut H) {
            random::<bool>().hash(state)
        }
    }

    pub fn run() {
        let mut s = HashSet::<Wobbly>::new();
        for _ in 0..12 {
            s.insert(Wobbly); // anything but UB can happen here!!
        }
    }
}

mod i_am_innocent {
    pub struct Opaque(Vec<i32>);
    impl Opaque {
        pub fn new() -> Self {
            Opaque(vec![1, 2, 3, 4])
        }
        pub fn second_element(&self) -> i32 {
            // Should be sound, because we know `self.0` has four elements:
            *(unsafe { self.0.get_unchecked(1) })
            // But how can we be really sure? It is unspecified what
            // `s.insert(Wobbly)` can do! It might cause other `Vec`s losing
            // elements, for example (e.g. because `HashSet` and `Vec` could
            // share a thread-local state, which gets corrupted by
            // `s.insert(Wobbly)`).
        }
    }
}

fn main() {
    let opaque = i_am_innocent::Opaque::new();
    i_am_not_unsafe::run(); // anything but UB can happen here!!
    println!("{}", opaque.second_element());
}

```

([Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ba4ac98280ff9454f872e9b17eeb20e0))

I am aware that this is a rather hypothetical problem, and will not pose any issue in practice. Moreover, the Rust reference is [non-normative](https://doc.rust-lang.org/stable/reference/#what-the-reference-is-not). (Not sure about the documentation of `std` though.)

Nonetheless, I believe that both the `std` doc and the Rust reference should specify more precisely which errors may happen when "logic errors" are made in the implementation of [`Hash`](https://doc.rust-lang.org/std/hash/trait.Hash.html) and [`Eq`](https://doc.rust-lang.org/std/cmp/trait.Eq.html) when using the data structures provided by [`std::collections`](https://doc.rust-lang.org/std/collections/index.html).

Given the Playground example above, the module `i_am_not_unsafe` would make reasoning in the module `i_am_innocent` pretty much impossible, thus formally leading to UB (even if that won't happen in practice).

If instead, the documentation provides a finite list of undesired effects that may happen in case of these "logic errors" regarding the use of `HashSet`, then module `i_am_innocent` can be sound. Such finite list might look as follows:

> _Edited version_
> 
> It is a logic error for an item to be modified in such a way that the item’s hash, as determined by the [`Hash`](https://doc.rust-lang.org/std/hash/trait.Hash.html) trait, or its equality, as determined by the [`Eq`](https://doc.rust-lang.org/std/cmp/trait.Eq.html) trait, changes while it is in the set. This is normally only possible through [`Cell`](https://doc.rust-lang.org/std/cell/struct.Cell.html), [`RefCell`](https://doc.rust-lang.org/std/cell/struct.RefCell.html), global state, I/O, or unsafe code. The behavior resulting from such a logic error is not specified ~~(it could include panics, incorrect results, aborts, memory leaks, or non-termination) but will not be undefined behavior.~~ _but will be one of the following:_
> 
> - _deadlocks / non-termination_
> - _panics_
> - _aborts_
> - _memory or other resource leaks_
> - _retrieval of wrong values / wrong behavior regarding the contents of the data stucture_
> 
> _It is guranteed that no other instances of the same collection type or other types are affected._

The reference should be updated accordingly as well.

For a genesis of my considerations refer to the thread on URLO linked in the top of this post.

---

<div class="post-metadata">

**Author:** ![steffahn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/steffahn/32/13288_2.png) [@steffahn](https://internals.rust-lang.org/u/steffahn)\
**Post date:** [May 17, 2022, 10:42am UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/2 "2022-05-17T10:42:01Z")

</div>

> [@jbe](#):
>
> > _It is guranteed that no other instances of the same collection type or other types are affected._

I suppose, this is the key point, and I feel like you’re completely right in saying something like this should be added. An exhaustive list of possible effects is not really necessary, at least for your code example.

Naturally and to be precise, it _is_ of course possible that other instances of the same collection / etc… are affected, but _only if_ the behavior of the `Hash` or `Eq` implementations or the `Hasher` are such that shared or global state is modified.

* * *

> [@jbe](#):
>
> logic error is not specified […] _but will be one of the following:_

Regarding an explicit list of possible effects, I would actually agree that that’s – whilst being a separate/different request/idea – also a reasonable thing to ask for; I do believe it’s possible to give a list of possible effects without restricting future algorithm design decisions. I’d also question whether or not memory leaks are supposed to be okay. (All of course excluding effects of the `Hash`/`Eq`/`Hasher` themselves which _can_ cause memory leaks, etc… as long as they do it directly.) AFAIK, memory leaks are usually only considered acceptable behavior if they’re explicitly created, created via (reference) cycles, or a consequence of other memory leaks (e.g. pre-poop-your-pants style); I’m not sure what’s gained by including logic errors into this list.

Of course, any algorithm relying on the reasonable behavior or a logically erraneous `HashMap` itself _can_ be buggy due to the erraneous `HashMap` and thus cause any cause of misbehavior, including – if `unsafe` code is involved – unsoundness / UB. But that’s a different thing.

---

<div class="post-metadata">

**Author:** ![2e71828](https://avatars.discourse-cdn.com/v4/letter/2/3e96dc/32.png) [@2e71828](https://internals.rust-lang.org/u/2e71828)\
**Post date:** [May 17, 2022, 11:20am UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/3 "2022-05-17T11:20:28Z")

</div>

> [@steffahn](#):
>
> I’d also question whether or not memory leaks are supposed to be okay. […] I’m not sure what’s gained by including logic errors into this list.

I could imagine an array-based collection losing track of whether or not a slot is occupied due to a logic bug in the comparison function. If this happens, the only safe thing to do is deallocate the slot without calling a destructor.

---

<div class="post-metadata">

**Author:** ![simonbuchan](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/simonbuchan/32/9390_2.png) [@simonbuchan](https://internals.rust-lang.org/u/simonbuchan)\
**Post date:** [May 17, 2022, 11:30am UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/4 "2022-05-17T11:30:07Z")

</div>

I'm not sure how this could happen and not be a safety hole?

---

<div class="post-metadata">

**Author:** ![steffahn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/steffahn/32/13288_2.png) [@steffahn](https://internals.rust-lang.org/u/steffahn)\
**Post date:** [May 17, 2022, 11:30am UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/5 "2022-05-17T11:30:14Z")

</div>

This assumes the collection notices that it’s “losing track of” stuff “due to a logic bug”, and hence chooses a safe approach. In particular it must notice the logic bug in the first place.

As far as my imagination goes, logic errors have unpredictable results precisely when algorithms rely on the properties of, say, a comparison function _without_ being able to verify those properties, and _without_ being able to tell when such a logic bug happened, which would then mean that (for soundness) whether or not something needs to be deallocated can’t be tracked using the behavior of the comparison function, because there’s no way for the collection to detect the logic bug. TL;DR, I can’t imagine any (more or less) concrete scenario where a collection can prevent double-frees but can’t prevent memory leaks.

---

<div class="post-metadata">

**Author:** ![simonbuchan](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/simonbuchan/32/9390_2.png) [@simonbuchan](https://internals.rust-lang.org/u/simonbuchan)\
**Post date:** [May 17, 2022, 11:31am UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/6 "2022-05-17T11:31:10Z")

</div>

Jinx, you owe me a coke.

---

<div class="post-metadata">

**Author:** ![jbe](https://avatars.discourse-cdn.com/v4/letter/j/f6c823/32.png) [@jbe](https://internals.rust-lang.org/u/jbe)\
**Post date:** [May 17, 2022, 11:42am UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/7 "2022-05-17T11:42:38Z")

</div>

I believe that:

If a "logic error" causes behavior that isn't clearly bounded to a certain set of things that may happen, you cannot use `unsafe` anywhere soundly (because I don't see how you could reason about any preconditions being true).

_Edit: Of course, formally "anything but UB" is such a set, but it's not sufficient as shown in the Playground example in my OP._

There might be some crate-level isolation, as it's impossible for one crate to thwart the guarantees of another crate if those crates don't use each other (and if there is no unsound use of `unsafe`), right?. But if `std` claims that anything but UB can happen, then this would certainly include effects on `std::Vec` (unless stated otherwise).

> [@steffahn](#):
>
> I suppose, this is the key point, and I feel like you’re completely right in saying something like this should be added. An exhaustive list of possible effects is not really necessary, at least for your code example.

I'm not sure if it's possible to state something like "this only affects the instance of the `HashSet` and not any other values in your program" without giving a finite list of things that are covered. I feel like not having a finite list makes reasoning about code much more hard (if not impossible).

---

<div class="post-metadata">

**Author:** ![steffahn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/steffahn/32/13288_2.png) [@steffahn](https://internals.rust-lang.org/u/steffahn)\
**Post date:** [May 17, 2022, 11:51am UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/8 "2022-05-17T11:51:43Z")

</div>

> [@jbe](#):
>
> > [@steffahn](#):
> >
> > I suppose, this is the key point, and I feel like you’re completely right in saying something like this should be added. An exhaustive list of possible effects is not really necessary, at least for your code example.
> 
> I'm not sure if it's possible to state something like "this only affects the instance of the `HashSet` and not any other values in your program" without giving a finite list of things that are covered. I feel like not having a finite list makes reasoning about code much more hard (if not impossible).

I guess my main point is that the “_gurantee[…] that no other instances of the same collection type or other types are affected_” is the most important point\[1\] and the least controversial, compared to the task of compiling a good exhaustive list of things-that-could-happenTM. As noted I agree that the latter does have good value, too, but the first alone would already be a significant improvement in the documentation, and it should be – as noted – completely uncontraversial. (Provided, it’s slightly rephrased to point out that side-effects of the `Eq`/`Hash`/`Hasher` implementation themselves _can_ influence other instances of collections.)

* * *

1. assuming that all of this language is ultimately _informal_ anyway, so I wouldn’t really let an argument of “as long as you don’t provide a finite list, anything can happen, hence this point doesn’t achieve anything at all” count, even if it might be logically correct

---

<div class="post-metadata">

**Author:** ![2e71828](https://avatars.discourse-cdn.com/v4/letter/2/3e96dc/32.png) [@2e71828](https://internals.rust-lang.org/u/2e71828)\
**Post date:** [May 17, 2022, 11:53am UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/9 "2022-05-17T11:53:25Z")

</div>

> [@steffahn](#):
>
> I can’t imagine any (more or less) concrete scenario where a collection can prevent double-frees but can’t prevent memory leaks.

To be honest, neither can I. But I've (tried to) read enough algorithms papers to know that there are people much cleverer than I coming up with this stuff— Leaking memory to maintain safety in unusual situations feels like a tool that we shouldn't prevent them from using.

---

<div class="post-metadata">

**Author:** ![jbe](https://avatars.discourse-cdn.com/v4/letter/j/f6c823/32.png) [@jbe](https://internals.rust-lang.org/u/jbe)\
**Post date:** [May 17, 2022, 12:14pm UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/10 "2022-05-17T12:14:55Z")

</div>

> [@steffahn](#):
>
> As noted I agree that the latter does have good value, too, but the first alone would already be a significant improvement in the documentation, and it should be – as noted – completely uncontraversial.

I think whether it's uncontraversial depends on whether it's possible to find a precise wording for "anything related(?) to the `HashSet` instance can happen". It might be a more difficult task than being more concrete (but not sure really).

**P.S.:** Maybe it would help to propose a particular wording, so discussing about it is easier. But I feel unable to come up with one (mostly because I feel insecure with terminology in Rust, such as "instance", etc).

**P.P.S.:** And perhaps, if we find such wording, it is effectively the same as such a "good exhaustive list of things-that-could-happen™, even if the list might not be stated explicitly.

---

<div class="post-metadata">

**Author:** ![steffahn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/steffahn/32/13288_2.png) [@steffahn](https://internals.rust-lang.org/u/steffahn)\
**Post date:** [May 17, 2022, 12:30pm UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/11 "2022-05-17T12:30:03Z")

</div>

> [@jbe](#):
>
> Maybe it would help to propose a particular wording, so discussing about it is easier. But I feel unable to come up with one (mostly because I feel insecure with terminology in Rust, such as "instance", etc).

Maybe something about absence of and/or effects on “global state” might be enough 🤔

Though “shared mutable state” between different `HashMap` (beyond anything that’s part of keys, values, or hashers), e.g. one created as a clone from the other, should probably be ruled out, too, because that wouldn’t be “global”, but still problematic.

---

<div class="post-metadata">

**Author:** ![2e71828](https://avatars.discourse-cdn.com/v4/letter/2/3e96dc/32.png) [@2e71828](https://internals.rust-lang.org/u/2e71828)\
**Post date:** [May 17, 2022, 1:12pm UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/12 "2022-05-17T13:12:39Z")

</div>

> [@jbe](#):
>
> Maybe it would help to propose a particular wording, so discussing about it is easier.

How about something like this:

> It is a logic error for an item to be modified in such a way that the item’s hash, as determined by the [`Hash`](https://doc.rust-lang.org/std/hash/trait.Hash.html) trait, or its equality, as determined by the [`Eq`](https://doc.rust-lang.org/std/cmp/trait.Eq.html) trait, changes while it is in the set. This is normally only possible through [`Cell`](https://doc.rust-lang.org/std/cell/struct.Cell.html), [`RefCell`](https://doc.rust-lang.org/std/cell/struct.RefCell.html), global state, I/O, or unsafe code.
> 
> If such a modification occurs, future behavior of the containing `HashMap` will be unpredictable and cannot be relied upon, with the following exceptions:
> 
> - The `HashSet` will never directly perform any unsound actions.
> - The `HashSet` will never access, modify, or return values which have not been inserted into the set.

---

<div class="post-metadata">

**Author:** ![jbe](https://avatars.discourse-cdn.com/v4/letter/j/f6c823/32.png) [@jbe](https://internals.rust-lang.org/u/jbe)\
**Post date:** [May 17, 2022, 1:49pm UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/13 "2022-05-17T13:49:33Z")

</div>

I like it. (But you accidentally mixed `HashMap` and `HashSet` in the example.)

I have no idea if it is "formally" a 100% precise description of what can happen, but I would assume it's at least on the same level as most other documentation of Rust.

For clarity, a _non-exhaustive_ positive list containing non-termination, panics, aborts and (optionally?) resource leaks could still be added. Something like:

> If such a modification occurs, future behavior of the containing `HashSet` will be unpredictable and cannot be relied upon, with the following exceptions:
> 
> - The `HashSet` will never directly perform any unsound actions.
> - The `HashSet` will never access, modify, or return values which have not been inserted into the set.
> 
> In particular, also panic, aborts, memory leaks, or non-termination may occur.

It ~~should~~ _could_ be clear that the list isn't exhaustive.

---

<div class="post-metadata">

**Author:** ![kpreid](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kpreid/32/8484_2.png) [@kpreid](https://internals.rust-lang.org/u/kpreid)\
**Post date:** [May 17, 2022, 3:33pm UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/14 "2022-05-17T15:33:51Z")

</div>

One way to look at this problem is in terms of _causality_ and _encapsulation_. The things which make UB uniquely bad are:

- UB may cause an effect that is “action at a distance” from the source of it: it can arbitrarily modify any other part of the program state, so the consequences **do not follow the laws of causality** that programmers normally assume or that language and library documentation promises, such as (especially with Rust's single-ownership rules) a value not observably changing unless modified by its owner, a borrow, or interior mutability.
- UB can **violate invariants across abstraction boundaries** : for example, corrupting the contents of private fields of a struct such that it no longer obeys the invariants maintained by the code to which those fields are visible. Narrowly, we can identify this as “encapsulation”; we could also consider it unwanted causality that bypassed the code that wanted to maintain the invariant.

So, I propose that where HashMap currently says “will not result in undefined behavior”, the spirit of the message is “will not violate causality or encapsulation”. We declare it will not violate causality, meaning that it does not affect what a programmer would consider _unrelated_ data; we declare it will not violate encapsulation, meaning that even the things that it interacts with will not enter states that they cannot have entered _merely by the HashMap returning values which are valid by its function signatures_.

Hmm, that last clause suggests a more formal condition: The HashMap may do anything that is all of:

1. not-UB / memory-safe,
2. **implied by its function signatures** (return wrong values, panic, loop, etc.), and
3. does not otherwise affect any state (except by returning results or calling callback functions according to point 2) **that would not be dropped when the HashMap is**.

(And the usual broad exception for the memory allocator's amount of free or used memory as a piece of shared state.)

---

<div class="post-metadata">

**Author:** ![2e71828](https://avatars.discourse-cdn.com/v4/letter/2/3e96dc/32.png) [@2e71828](https://internals.rust-lang.org/u/2e71828)\
**Post date:** [May 17, 2022, 4:25pm UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/15 "2022-05-17T16:25:08Z")

</div>

> [@kpreid](#):
>
> not-UB

There's quite a bit of cognitive dissonance in the statement that "The `HashMap`'s behavior is unspecified, but it will not perform any **U** nspecified **B** ehavior."

I used the term _unsound_ to get around this problem; other options might be:

- "compiler-UB" (to distinguish from library-UB, which we _do_ have here)
- "memory/thread safety"
- "operations forbidden by the language"

On the other hand, "UB" might be strong enough jargon that this isn't an issue in practice.

---

<div class="post-metadata">

**Author:** ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)\
**Post date:** [May 17, 2022, 4:31pm UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/16 "2022-05-17T16:31:17Z")

</div>

> [@2e71828](#):
>
> > [@kpreid](#):
> >
> > not-UB
> 
> There's quite a bit of cognitive dissonance in the statement that "The `HashMap` 's behavior is unspecified, but it will not perform any **U** nspecified **B** ehavior."

UB is _undefined_ behavior, which has a different formal meaning than unspecified.

---

<div class="post-metadata">

**Author:** ![jbe](https://avatars.discourse-cdn.com/v4/letter/j/f6c823/32.png) [@jbe](https://internals.rust-lang.org/u/jbe)\
**Post date:** [May 17, 2022, 4:32pm UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/17 "2022-05-17T16:32:20Z")

</div>

I'm not an expert, but to my understanding undefined behavior (UB) can cause _any_ program behavior (by definition), while unspecified behavior requires further context and isn't well defined.

---

<div class="post-metadata">

**Author:** ![kpreid](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kpreid/32/8484_2.png) [@kpreid](https://internals.rust-lang.org/u/kpreid)\
**Post date:** [May 17, 2022, 4:46pm UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/18 "2022-05-17T16:46:42Z")

</div>

To clarify: when I wrote “not-UB”, I meant the acronym “_undefined behavior_” which I consider to have a clear-enough formal definition. The rest of my post was attempting to define a _useful_ boundary around behavior which is _not_ formal undefined behavior, but also is not specified to be anything in particular — useful in that programmers can actually write programs which avoid having problems caused by that mis-behavior.

---

<div class="post-metadata">

**Author:** ![jbe](https://avatars.discourse-cdn.com/v4/letter/j/f6c823/32.png) [@jbe](https://internals.rust-lang.org/u/jbe)\
**Post date:** [May 17, 2022, 5:28pm UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/19 "2022-05-17T17:28:47Z")

</div>

I use UB for "_undefined_ behavior", which is the common abbreviation, I believe.

> [@kpreid](#):
>
> behavior which is _not_ formal undefined behavior, but also is not specified to be anything in particular

I would claim that such behavior (behavior "which is not UB but not specified to be anything in particular") isn't much helpful. If there's no clear definition on what can happen and what not, then it's UB (by definition). The only way out would be to say "anything but UB", which means "anything but at least one thing can happen", but the practical value of such definition is very limited, I think.

Therefore, in one or the other way, it needs to be _specified_ what exactly is _unspecified_ when `Hash` and `Eq` are implemented wrong and used in one of the `std::collections` that rely on them.

Whether that specification needs to be a finite list or some more abstract description, I'm not sure. But there needs to be at least some sort of clarity, as otherwise it would practically be the same as UB.

---

<div class="post-metadata">

**Author:** ![kpreid](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kpreid/32/8484_2.png) [@kpreid](https://internals.rust-lang.org/u/kpreid)\
**Post date:** [May 17, 2022, 5:41pm UTC](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640/20 "2022-05-17T17:41:43Z")

</div>

> [@jbe](#):
>
> Therefore, in one or the other way, it needs to be _specified_ what exactly is _unspecified_

That's what I meant to propose: I wrote

> [@kpreid](#):
>
> will not violate causality or encapsulation

along with a definition of the phrase, which is a very strong upper bound on what can happen, no matter what algorithm the HashMap (or any other type of interest) uses internally and how it goes wrong.

[Next page](https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640.md?page=2)
