Skip to content

Commit 9e36111

Browse files
committedOct 6, 2017
Implement entry_and_modify
1 parent 183329c commit 9e36111

File tree

3 files changed

+146
-0
lines changed

3 files changed

+146
-0
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# `entry_and_modify`
2+
3+
The tracking issue for this feature is: [#44733]
4+
5+
[#44733]: https://github.com/rust-lang/rust/issues/44733
6+
7+
------------------------
8+
9+
This introduces a new method for the Entry API of maps
10+
(`std::collections::HashMap` and `std::collections::BTreeMap`), so that
11+
occupied entries can be modified before any potential inserts into the
12+
map.
13+
14+
For example:
15+
16+
```rust
17+
#![feature(entry_and_modify)]
18+
# fn main() {
19+
use std::collections::HashMap;
20+
21+
struct Foo {
22+
new: bool,
23+
}
24+
25+
let mut map: HashMap<&str, Foo> = HashMap::new();
26+
27+
map.entry("quux")
28+
.and_modify(|e| e.new = false)
29+
.or_insert(Foo { new: true });
30+
# }
31+
```
32+
33+
This is not possible with the stable API alone since inserting a default
34+
_before_ modifying the `new` field would mean we would lose the default state:
35+
36+
```rust
37+
# fn main() {
38+
use std::collections::HashMap;
39+
40+
struct Foo {
41+
new: bool,
42+
}
43+
44+
let mut map: HashMap<&str, Foo> = HashMap::new();
45+
46+
map.entry("quux").or_insert(Foo { new: true }).new = false;
47+
# }
48+
```
49+
50+
In the above code the `new` field will never be `true`, even though we only
51+
intended to update that field to `false` for previously extant entries.
52+
53+
To achieve the same effect as `and_modify` we would have to manually match
54+
against the `Occupied` and `Vacant` variants of the `Entry` enum, which is
55+
a little less user-friendly, and much more verbose:
56+
57+
```rust
58+
# fn main() {
59+
use std::collections::HashMap;
60+
use std::collections::hash_map::Entry;
61+
62+
struct Foo {
63+
new: bool,
64+
}
65+
66+
let mut map: HashMap<&str, Foo> = HashMap::new();
67+
68+
match map.entry("quux") {
69+
Entry::Occupied(entry) => {
70+
entry.into_mut().new = false;
71+
},
72+
Entry::Vacant(entry) => {
73+
entry.insert(Foo { new: true });
74+
},
75+
};
76+
# }
77+
```

‎src/liballoc/btree/map.rs

+34
Original file line numberDiff line numberDiff line change
@@ -2102,6 +2102,40 @@ impl<'a, K: Ord, V> Entry<'a, K, V> {
21022102
Vacant(ref entry) => entry.key(),
21032103
}
21042104
}
2105+
2106+
/// Provides in-place mutable access to an occupied entry before any
2107+
/// potential inserts into the map.
2108+
///
2109+
/// # Examples
2110+
///
2111+
/// ```
2112+
/// #![feature(entry_and_modify)]
2113+
/// use std::collections::BTreeMap;
2114+
///
2115+
/// let mut map: BTreeMap<&str, usize> = BTreeMap::new();
2116+
///
2117+
/// map.entry("poneyland")
2118+
/// .and_modify(|e| { *e += 1 })
2119+
/// .or_insert(42);
2120+
/// assert_eq!(map["poneyland"], 42);
2121+
///
2122+
/// map.entry("poneyland")
2123+
/// .and_modify(|e| { *e += 1 })
2124+
/// .or_insert(42);
2125+
/// assert_eq!(map["poneyland"], 43);
2126+
/// ```
2127+
#[unstable(feature = "entry_and_modify", issue = "44733")]
2128+
pub fn and_modify<F>(self, mut f: F) -> Self
2129+
where F: FnMut(&mut V)
2130+
{
2131+
match self {
2132+
Occupied(mut entry) => {
2133+
f(entry.get_mut());
2134+
Occupied(entry)
2135+
},
2136+
Vacant(entry) => Vacant(entry),
2137+
}
2138+
}
21052139
}
21062140

21072141
impl<'a, K: Ord, V: Default> Entry<'a, K, V> {

‎src/libstd/collections/hash/map.rs

+35
Original file line numberDiff line numberDiff line change
@@ -2002,6 +2002,41 @@ impl<'a, K, V> Entry<'a, K, V> {
20022002
Vacant(ref entry) => entry.key(),
20032003
}
20042004
}
2005+
2006+
/// Provides in-place mutable access to an occupied entry before any
2007+
/// potential inserts into the map.
2008+
///
2009+
/// # Examples
2010+
///
2011+
/// ```
2012+
/// #![feature(entry_and_modify)]
2013+
/// use std::collections::HashMap;
2014+
///
2015+
/// let mut map: HashMap<&str, u32> = HashMap::new();
2016+
///
2017+
/// map.entry("poneyland")
2018+
/// .and_modify(|e| { *e += 1 })
2019+
/// .or_insert(42);
2020+
/// assert_eq!(map["poneyland"], 42);
2021+
///
2022+
/// map.entry("poneyland")
2023+
/// .and_modify(|e| { *e += 1 })
2024+
/// .or_insert(42);
2025+
/// assert_eq!(map["poneyland"], 43);
2026+
/// ```
2027+
#[unstable(feature = "entry_and_modify", issue = "44733")]
2028+
pub fn and_modify<F>(self, mut f: F) -> Self
2029+
where F: FnMut(&mut V)
2030+
{
2031+
match self {
2032+
Occupied(mut entry) => {
2033+
f(entry.get_mut());
2034+
Occupied(entry)
2035+
},
2036+
Vacant(entry) => Vacant(entry),
2037+
}
2038+
}
2039+
20052040
}
20062041

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

0 commit comments

Comments
 (0)
Please sign in to comment.