rust-lang / rust-lang/rfcs

pre-RFC: Add a way to insert into a collection, then return a reference to the inserted item in one operation.

Open
#3,343 8 comments 18 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Markdown
Stars
6.6k
Forks
1.7k
Avg merge
16h 14m
Merged PRs (30d)
1

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 requires hashbrown. 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 and LinkedList already have front and back methods. Do they make push_front_get and push_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>))

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Begin with the proposed collection APIs in the issue and review the linked #82766 discussion; no implementation file or test is named. Done means settling the API names, return types, and *_get_mut/key-reference questions in an accepted RFC before implementation.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
developer-experience
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.