Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement and_modify on Entry #44734

Merged
merged 1 commit into from
Oct 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions src/doc/unstable-book/src/library-features/entry-and-modify.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# `entry_and_modify`

The tracking issue for this feature is: [#44733]

[#44733]: https://github.com/rust-lang/rust/issues/44733

------------------------

This introduces a new method for the Entry API of maps
(`std::collections::HashMap` and `std::collections::BTreeMap`), so that
occupied entries can be modified before any potential inserts into the
map.

For example:

```rust
#![feature(entry_and_modify)]
# fn main() {
use std::collections::HashMap;

struct Foo {
new: bool,
}

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

map.entry("quux")
.and_modify(|e| e.new = false)
.or_insert(Foo { new: true });
# }
```

This is not possible with the stable API alone since inserting a default
_before_ modifying the `new` field would mean we would lose the default state:

```rust
# fn main() {
use std::collections::HashMap;

struct Foo {
new: bool,
}

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

map.entry("quux").or_insert(Foo { new: true }).new = false;
# }
```

In the above code the `new` field will never be `true`, even though we only
intended to update that field to `false` for previously extant entries.

To achieve the same effect as `and_modify` we would have to manually match
against the `Occupied` and `Vacant` variants of the `Entry` enum, which is
a little less user-friendly, and much more verbose:

```rust
# fn main() {
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("quux") {
Entry::Occupied(entry) => {
entry.into_mut().new = false;
},
Entry::Vacant(entry) => {
entry.insert(Foo { new: true });
},
};
# }
```
34 changes: 34 additions & 0 deletions src/liballoc/btree/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2102,6 +2102,40 @@ impl<'a, K: Ord, V> Entry<'a, K, V> {
Vacant(ref entry) => entry.key(),
}
}

/// Provides in-place mutable access to an occupied entry before any
/// potential inserts into the map.
///
/// # Examples
///
/// ```
/// #![feature(entry_and_modify)]
/// use std::collections::BTreeMap;
///
/// let mut map: BTreeMap<&str, usize> = BTreeMap::new();
///
/// map.entry("poneyland")
/// .and_modify(|e| { *e += 1 })
/// .or_insert(42);
/// assert_eq!(map["poneyland"], 42);
///
/// map.entry("poneyland")
/// .and_modify(|e| { *e += 1 })
/// .or_insert(42);
/// assert_eq!(map["poneyland"], 43);
/// ```
#[unstable(feature = "entry_and_modify", issue = "44733")]
pub fn and_modify<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),
}
}
}

impl<'a, K: Ord, V: Default> Entry<'a, K, V> {
Expand Down
35 changes: 35 additions & 0 deletions src/libstd/collections/hash/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2002,6 +2002,41 @@ impl<'a, K, V> Entry<'a, K, V> {
Vacant(ref entry) => entry.key(),
}
}

/// Provides in-place mutable access to an occupied entry before any
/// potential inserts into the map.
///
/// # Examples
///
/// ```
/// #![feature(entry_and_modify)]
/// use std::collections::HashMap;
///
/// let mut map: HashMap<&str, u32> = HashMap::new();
///
/// map.entry("poneyland")
/// .and_modify(|e| { *e += 1 })
/// .or_insert(42);
/// assert_eq!(map["poneyland"], 42);
///
/// map.entry("poneyland")
/// .and_modify(|e| { *e += 1 })
/// .or_insert(42);
/// assert_eq!(map["poneyland"], 43);
/// ```
#[unstable(feature = "entry_and_modify", issue = "44733")]
pub fn and_modify<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),
}
}

}

impl<'a, K, V: Default> Entry<'a, K, V> {
Expand Down