rust-lang / rust-lang/libs-team

Create iterator function in std libs: split_item_mut()

Open
#295 9 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

api-change-proposal
Dominant language
Rust
Stars
178
Forks
28
Avg merge
15m
Merged PRs (30d)
1

Description

Proposal

Problem statement

If I want to use a mutable vector to iterate over the same vector. I can't do this without the borrow checker starts to complain about the vector already being borrowed. There is no user friendly alternative / ergonomic solution for this problem. A possible problem:

Motivating examples or use cases

for particle in particles.iter_mut() {
    for other in particles.iter_mut() {
         if particle != other {
             particle.velocity += other.calculate_gravity(&particle);
         }
    }
}

This is something the borrow checker won't allow. Of course there are options to fix this like updating the index in the vector immediately or using something like swap_remove, both solutions are not very ergonomic / user friendly in my opinion.

Solution sketch

A possible solution is:

pub trait Splitting<T> {
    fn split_item_mut(&mut self, idx: usize) -> (&mut[T], &mut T, &mut [T]);
}

impl<T> Splitting<T> for Vec<T> {
    fn split_item_mut(&mut self, idx: usize) -> (&mut T, OtherIterMut<T>) {
        assert!(idx < self.len());

        let (head, rest) = self.split_at_mut(idx);

        let (item, tail) = rest.split_first_mut().unwrap();

        (head, item, tail)
    }
}

And to use it:

let (head, item, tail) = some_vector.split_item_mut(some_index);

// Or chain...
for other in head.iter_mut() {
     other.some_field = item.some_field;
}

for other in tail.iter_mut() {
     other.some_field = item.some_field;
}

Of course this isn't a boundry safe API so you can think of returning the "item" in the tuple as an Option.

Alternatives

pub type OtherIterMut<'a, T> = std::iter::Chain<IterMut<'a, T>, IterMut<'a, T>>;

pub trait Splitting<T> {
    fn split_item_mut(&mut self, idx: usize) -> (&mut T, OtherIterMut<T>);
}

impl<T> Splitting<T> for Vec<T> {
    fn split_item_mut(&mut self, idx: usize) -> (&mut T, OtherIterMut<T>) {
        assert!(idx < self.len());

        let (head, rest) = self.split_at_mut(idx);

        let (item, tail) = rest.split_first_mut().unwrap();

        let others = head.iter_mut().chain(tail.iter_mut());

        (item, others)
    }
}

And to use it:

let (item, others) = some_vector.split_item_mut(some_index);
for other in others {
     other.some_field = item.some_field;
}

Links and related work

Also posted on: https://internals.rust-lang.org/t/create-iterator-function-in-std-libs-split-item-mut/19880

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

Start by reading the proposal and its linked Rust Internals discussion, then compare the split_at_mut, split_first_mut, and iter_mut usage shown in the examples. No implementation file or test is named; done would require settling the API shape and documenting the accepted behavior before implementation and validation.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.