-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Missing a good way to insert into a HashMap while panicking on duplicates #3092
Comments
I like |
I'd expect it to be something like this: /// Tries to insert a key-value pair into the map, and returns
/// a mutable reference to the value in the entry.
///
/// If the map already had this key present, nothing is updated, and
/// an error containing the occupied entry and the value is returned.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::collections::BTreeMap;
///
/// let mut map = BTreeMap::new();
/// assert_eq!(map.try_insert(37, "a").unwrap(), "a");
///
/// let err = map.try_insert(37, "b").unwrap_err();
/// assert_eq!(err.entry.key(), 37);
/// assert_eq!(err.entry.get(), "a");
/// assert_eq!(err.value, "b");
/// ```
pub fn try_insert(&mut self, key: K, value: V) -> Result<&mut V, OccupiedError<'_, K, V>>; Working on an implementation now to try this out. |
In principle I like |
@BurntSushi Yes. Taking a look at all the different Unfortunately we can't change or rename |
If the |
Before, with After, with |
Implementation: rust-lang/rust#82764 |
possible alternative names: |
🌈 Bikeshed! 🌈 We could take MySQL and SQLite's terminology and use Redis called this operation " C++ calls this try_emplace (thus similar to Java calls this putIfAbsent (thus similar to Or maybe just call this map.occupy(key, val).unwrap(); |
A few more alternatives: ( |
|
I still feel that |
Since we are in the unfortunate situation that the function called There are some great names mentioned in the posts above to pick from. My personal favourites are |
Would it be technically possible to have the |
@tanriol Different editions still need to use the same standard library. And yes, even if we could do it, we shouldn't. |
Before anything, I'm not at all against However, for the top-post-described problem, I think the currently-possible |
@Ekleog |
Totally agreed, I'm only talking about the problem described in the top-post of replacing But I'm pretty sure there are other problems that are solved by |
Add {BTreeMap,HashMap}::try_insert `{BTreeMap,HashMap}::insert(key, new_val)` returns `Some(old_val)` if the key was already in the map. It's often useful to assert no duplicate values are inserted. We experimented with `map.insert(key, val).unwrap_none()` (rust-lang#62633), but decided that that's not the kind of method we'd like to have on `Option`s. `insert` always succeeds because it replaces the old value if it exists. One could argue that `insert()` is never the right method for panicking on duplicates, since already handles that case by replacing the value, only allowing you to panic after that already happened. This PR adds a `try_insert` method that instead returns a `Result::Err` when the key already exists. This error contains both the `OccupiedEntry` and the value that was supposed to be inserted. This means that unwrapping that result gives more context: ```rust map.insert(10, "world").unwrap_none(); // thread 'main' panicked at 'called `Option::unwrap_none()` on a `Some` value: "hello"', src/main.rs:8:29 ``` ```rust map.try_insert(10, "world").unwrap(); // thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: // OccupiedError { key: 10, old_value: "hello", new_value: "world" }', src/main.rs:6:33 ``` It also allows handling the failure in any other way, as you have full access to the `OccupiedEntry` and the value. `try_insert` returns a reference to the value in case of success, making it an alternative to `.entry(key).or_insert(value)`. r? `@Amanieu` Fixes rust-lang/rfcs#3092
Add {BTreeMap,HashMap}::try_insert `{BTreeMap,HashMap}::insert(key, new_val)` returns `Some(old_val)` if the key was already in the map. It's often useful to assert no duplicate values are inserted. We experimented with `map.insert(key, val).unwrap_none()` (rust-lang#62633), but decided that that's not the kind of method we'd like to have on `Option`s. `insert` always succeeds because it replaces the old value if it exists. One could argue that `insert()` is never the right method for panicking on duplicates, since already handles that case by replacing the value, only allowing you to panic after that already happened. This PR adds a `try_insert` method that instead returns a `Result::Err` when the key already exists. This error contains both the `OccupiedEntry` and the value that was supposed to be inserted. This means that unwrapping that result gives more context: ```rust map.insert(10, "world").unwrap_none(); // thread 'main' panicked at 'called `Option::unwrap_none()` on a `Some` value: "hello"', src/main.rs:8:29 ``` ```rust map.try_insert(10, "world").unwrap(); // thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: // OccupiedError { key: 10, old_value: "hello", new_value: "world" }', src/main.rs:6:33 ``` It also allows handling the failure in any other way, as you have full access to the `OccupiedEntry` and the value. `try_insert` returns a reference to the value in case of success, making it an alternative to `.entry(key).or_insert(value)`. r? ``@Amanieu`` Fixes rust-lang/rfcs#3092
Add {BTreeMap,HashMap}::try_insert `{BTreeMap,HashMap}::insert(key, new_val)` returns `Some(old_val)` if the key was already in the map. It's often useful to assert no duplicate values are inserted. We experimented with `map.insert(key, val).unwrap_none()` (rust-lang/rust#62633), but decided that that's not the kind of method we'd like to have on `Option`s. `insert` always succeeds because it replaces the old value if it exists. One could argue that `insert()` is never the right method for panicking on duplicates, since already handles that case by replacing the value, only allowing you to panic after that already happened. This PR adds a `try_insert` method that instead returns a `Result::Err` when the key already exists. This error contains both the `OccupiedEntry` and the value that was supposed to be inserted. This means that unwrapping that result gives more context: ```rust map.insert(10, "world").unwrap_none(); // thread 'main' panicked at 'called `Option::unwrap_none()` on a `Some` value: "hello"', src/main.rs:8:29 ``` ```rust map.try_insert(10, "world").unwrap(); // thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: // OccupiedError { key: 10, old_value: "hello", new_value: "world" }', src/main.rs:6:33 ``` It also allows handling the failure in any other way, as you have full access to the `OccupiedEntry` and the value. `try_insert` returns a reference to the value in case of success, making it an alternative to `.entry(key).or_insert(value)`. r? ```@Amanieu``` Fixes rust-lang/rfcs#3092
HashMap::insert(key, val)
returnsSome(old_val)
as the 'error' if the key was already in the map. In many cases it's assumed no duplicate keys are ever added, but still asserted by panicking otherwise.We experimented with
map.insert(key, val).unwrap_none()
(rust-lang/rust#62633), but decided that that's not the kind of method we'd like to have onOption
s.Using an assert macro has some downsides:
Not everyone feels comfortable with assertions having side effects.
1
doesn't show any context in the panic message. (Thoughunwrap_none
also didn't show the key or the new value. Only the old value.)2
would only work if the value type implementsPartialEq
.3
uses a macro that doesn't exist (yet) instd
.Updating the
HashMap
interface might be a good solution:insert
always succeeds because it replaces the old value if it exists. One could argue thatinsert()
is never the right method for panicking on duplicates, since already handles that case by replacing the value, only allowing you to panic after that already happened.A
try_insert
method could instead return anResult::Err
indicating the duplicate key error, which could also report the full context (key, old value, new value) in the panic message.Alternatively (or additionally), the
Entry
interface could be updated to have a.unwrap_vacant()
method to assume the obtained entry is vacant, which can then be used to insert the value. This can report the key and the old value in the panic message.The text was updated successfully, but these errors were encountered: