-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Open
Description
I ran across this issue today when I'm attempting to write a function like this:
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct Player {
// fields omitted
}
struct PlayerList {
// other fields omitted
players: HashSet<Player>,
}
impl PlayerList {
/// Create a new player and insert it into the player list.
/// Then return a reference to this new player.
pub fn new_player(
&mut self,
// other fields omitted
) -> &Player {
let new = Player {};
self.players.insert(new);
todo!("return a reference to the new player")
}
}
To my surprise, there is no easy way to do this with HashSet
, or for that matter, with any collection. I ended up having to write this:
pub fn new_player(&mut self) -> &Player {
let new = Player {};
self.players.insert(new.clone());
self.players.get(&new).unwrap()
}
This is obviously bad for a multitude of reasons:
- Unnecessary
Player::clone
call - Unnecessary
Hashset::get
call - Extra
unwrap
- Generally bad ergonomics
So I asked on the Rust-lang discord guild here, and Yand.rs gave this solution:
use ::hashbrown::HashMap;
fn create_new(s: &mut HashMap<String, ()>) -> &str {
let new = String::from("…");
s.raw_entry_mut().from_key(&new).or_insert(new, ()).0
}
... but also had this to say:
Sadly it requires
raw_entry_mut()
, which is either unstable, or requireshashbrown
. And also only manual maps have these, sets don't for some reason
My proposition
Add a family of associated functions that provide this capability to all collections:
impl<T> Vec<T> {
pub fn insert_get(&mut self, index: usize, value: T) -> &T;
pub fn push_get(&mut self, value: T) -> &T;
}
impl<T> VecDeque<T> {
pub fn insert_get(&mut self, index: usize, value: T) -> &T;
// might be unnecessary, see questions
pub fn push_front_get(&mut self, value: T) -> &T;
// might be unnecessary, see questions
pub fn push_back_get(&mut self, value: T) -> &T;
}
impl<T> LinkedList<T> {
// might be unnecessary, see questions
pub fn push_front_get(&mut self, value: T) -> &T;
// might be unnecessary, see questions
pub fn push_back_get(&mut self, value: T) -> &T;
}
impl<K, V> HashMap<K, V> {
pub fn insert_get(&mut self, key: K, value: V) -> (&V, Option<V>);
// See https://github.com/rust-lang/rust/issues/82766#issuecomment-1301845589
pub fn insert_vacant_get(&mut self, key: K, value: V) -> (&V, Option<V>);
}
impl<K, V> BTreeMap<K, V> {
pub fn insert_get(&mut self, key: K, value: V) -> (&V, Option<V>);
// See https://github.com/rust-lang/rust/issues/82766#issuecomment-1301845589
pub fn insert_vacant_get(&mut self, key: K, value: V) -> (&V, Option<V>);
}
impl<T> HashSet<T> {
pub fn replace_get(&mut self, value: T) -> (&T, Option<T>);
}
impl<T> BTreeSet<T> {
pub fn replace_get(&mut self, value: T) -> (&T, Option<T>);
}
impl<T> BinaryHeap<T> {
pub fn push_get(&mut self, value: T) -> &T;
}
Potential questions
- The names are descriptive but not very elegant IMO. Are there better ones?
- Should there be
*_get_mut
variants too? -
VecDeque
andLinkedList
already havefront
andback
methods. Do they makepush_front_get
andpush_back_get
redundant? - In the return type of maps and sets, should the order of tuple members be flipped? (i.e.
(Option<V>, &V)
instead) - For maps, should the return type contain a reference to the key as well? (i.e.
(&K, &V, Option<V>)
or((&K, &V), Option<V>)
)
truppelito, littzhch, SOF3, bvanseg, rodrimati1992 and 12 more
Metadata
Metadata
Assignees
Labels
No labels