# pre-RFC: \`and\_then\` method on \`std::collections::hash\_map::Entry\`

**URL:** <https://internals.rust-lang.org/t/pre-rfc-and-then-method-on-std-collections-hash-map-entry/5918>\
**Category:** libs\
**Created:** [September 14, 2017, 7:24pm UTC](https://internals.rust-lang.org/t/pre-rfc-and-then-method-on-std-collections-hash-map-entry/5918 "2017-09-14T19:24:12Z")\
**Posts on this page:** 1\
**Showing post:** 2

<div class="post-metadata">

**Author:** ![mchlrhw](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mchlrhw/32/2696_2.png) [@mchlrhw](https://internals.rust-lang.org/u/mchlrhw)\
**Post date:** [September 14, 2017, 7:25pm UTC](https://internals.rust-lang.org/t/pre-rfc-and-then-method-on-std-collections-hash-map-entry/5918/2 "2017-09-14T19:25:01Z")

</div>

- Feature Name: hashmap\_entry\_and\_then
- Start Date: 2017-09-14
- RFC PR:
- Rust Issue:

# Summary

Add an `and_then` method to `std::collections::hash_map::Entry` to allow mutable access to an occupied entry’s value, before any potential calls to `or_insert`.

# Motivation

`Entry`s are useful for allowing access to existing values in a map while also allowing default values to be inserted for absent keys. The existing API is similar to that of `Option`, where `or` and `or_with` can be used if the option variant is `None`.

The `Entry` API is, however, missing an equivalent of `Option`'s `and_then` method. If it were present it would be possible to modify an existing entry _before_ calling `or_insert` without resorting to matching on the entry variant.

# Guide-level explanation

If a value in a `HashMap` is required to be modified before inserting a default it is possible to do so using `entry` in combination with `and_then` and `or_insert`. This allows modification of occupied entries to be chained with insertion in the case where the entry is vacant, as demonstrated below:

```rust
use std::collections::HashMap;

struct Foo {
    new: bool,
}   

let mut map: HashMap<&str, Foo> = HashMap::new();

map.entry("poneyland")
    .and_then(|e| *e.new = false)
    .or_insert(Foo { new: true })

assert_eq!(map["poneyland"].new, true)

```

Without `and_then` we would have to use `match` over the two `Entry` variants: `Occupied` and `Vacant`. For example:

```rust
use std::collections::HashMap;
use std::collections::hash_map::Entry;

struct Foo {
    new: bool,
}

let mut map: HashMap<&str, Foo> = HashMap::new();

match map.entry("poneyland") {
    Entry::Occupied(entry) => {
        entry.into_mut().new = false;
    },
    Entry::Vacant(entry) => {
        entry.insert(Foo { new: true });
    },
}

assert_eq!(map["poneyland"].new, true)

```

# Reference-level explanation

This feature has two constraints:

1. The new `and_then` method is designed to work alongside the functionality provided by `or_insert` and `or_insert_with`, therefore it must return an `Entry` so that the methods can be chained.
2. It must provide access to the contained value, so that the value can be mutated if necessary.

Reference implementation:

```rust
impl<'a, K, V> Entry<'a, K, V> {
    pub fn and_then<F>(self, mut f: F) -> Self
        where F: FnMut(&mut V)
    {
        match self {
            Occupied(mut entry) => {
                f(entry.get_mut());
                Occupied(entry)
            },
            Vacant(entry) => Vacant(entry),
        }
    }
}

```

The two constraints could also be satisfied if we require the user provided function to accept an `OccupiedEntry` and also return it. The user is then free to call `get_mut` on it in the function body in order to obtain a mutable borrow of the entry’s value.

Alternative implementation:

```rust
impl<'a, K, V> Entry<'a, K, V> {
    pub fn and_then<F>(self, f: F) -> Self
        where F: FnOnce(OccupiedEntry<K, V>) -> OccupiedEntry<K, V>
    {
        match self {
            Occupied(entry) => Occupied(f(entry)),
            Vacant(entry) => Vacant(entry),
        }
    }
}

```

This has the advantage that the closure is `FnOnce` as opposed to the more restrictive `FnMut`; however, the ergonomics for the user are slightly worse as it would have to be called like so:

```rust
map.entry("poneyland")
    .and_then(|mut e| { e.get_mut() += 1; e })
    .or_insert(42);

```

# Drawbacks

This logic can be implemented by matching on the entry variant, as described in the [Guide-level](#guide-level-explanation) section, and as such could be seen as an unneccessary addition to the API.

Another potential drawback is that this change to the API is specifically intended to solve the problem of mutating an existing value _before_ insertion of defaults, but it is conceivable that newcomers could use it instead of following more obvious patterns. For example:

```rust
use std::collections::HashMap;

let mut map: HashMap<&str, u32> = HashMap::new();

map.insert("poneyland", 0)

// Using `and_then` for simple mutation of an entry
map.entry("poneyland").and_then(|e| { *e += 1 });

// as opposed to just using the entry in place
*map.entry("poneyland") += 1;

```

However, the already good documentation of the hash lib should mitigate this risk.

It is also conceivable that newcomers to the API will attempt to use `and_then` and `or_insert` out of order, although the fact that `or_insert` does not return `Entry` should make it obvious how the two are supposed to interact and will result in a compile time error.

# Rationale and Alternatives

It is difficult to conceive of an alternative that fits the criteria of being compatible with existing methods (in order to support chaining) and also provide mutable access to the entry’s value. An alternative is provided in the [Reference-level](#reference-level-explanation) section, but is not as ergonomic for the user of the API.

Any alternative implementations would have to provide both mutable access to the held value and return the entry, which are hard to balance due to move semantics.

# Unresolved questions

Whether or not this change to the API adds significant value over simply using `match` and whether or not there are better alternative implementations that have been overlooked.

Out of scope for this RFC is the porting of other methods from `Option`, which may make the `Entry` API more user-friendly/flexible.

Also out of scope is the potential for an `or_insert` alternative (lets call it `or_inplace`, for example) to be implemented that returns `Entry` instead of `&'a mut V`, which would allow the chaining of `and_then` _after_ a call to `or_inplace`.

---

_[View the full topic](https://internals.rust-lang.org/t/pre-rfc-and-then-method-on-std-collections-hash-map-entry/5918)._
