-2

In JavaScript I would do this:

function move(arr, old_index, new_index) {
  while (old_index < 0) {
    old_index += arr.length;
  }
  while (new_index < 0) {
    new_index += arr.length;
  }
  if (new_index >= arr.length) {
    var k = new_index - arr.length;
    while ((k--) + 1) {
      arr.push(undefined);
    }
  }
  arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
  return arr;
}

How can I accomplish the same thing in Rust?

I don't want to use insert and remove because my vector is a std::vec::Vec<std::string::String> and I want to literally move them to a different location in the vector, not remove them and then insert a copy.

I don't want to swap 2 elements. I want to change the index of an element to an arbitrary other index, like a person cutting to some arbitrary other position in a queue.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
GirkovArpa
  • 4,427
  • 4
  • 14
  • 43
  • 1
    Does this answer your question? [How can I swap items in a vector, slice, or array in Rust?](https://stackoverflow.com/questions/25531963/how-can-i-swap-items-in-a-vector-slice-or-array-in-rust) – ShadowRanger Jun 02 '20 at 01:11
  • No, because I don't want to swap 2 elements. I want to change the index of an element, like a person cutting to some arbitrary position in a queue. – GirkovArpa Jun 02 '20 at 01:13
  • 1
    Sharing your research helps everyone. Tell us what you've tried and why it didn’t meet your needs. This demonstrates that you’ve taken the time to try to help yourself, it saves us from reiterating obvious answers, and most of all it helps you get a more specific and relevant answer. See also: [ask] – John Kugelman Jun 02 '20 at 01:14
  • I don't see any answers, much less any obvious or reiterated ones. Does Rust not have such an inbuilt function? – GirkovArpa Jun 02 '20 at 01:19
  • 1
    @GirkovArpa: Elements are stored at a specific memory location in a vector, and vectors are very low feature collections; the index is a specific offset from the beginning of the contiguously allocated memory. You can't change the index of an element without moving the element to a new memory location. At best you can make the vector store pointers to elements allocated elsewhere, and swap the pointers (which still moves memory, but not the entire element). What you're doing in JavaScript is still moving the references around, it's not magically changing the index of an unmoved element. – ShadowRanger Jun 02 '20 at 01:21
  • I see. So what I'm asking for is not technically possible. As a prerequisite I must use a vector of `&String` instead of `String`. Is that right? – GirkovArpa Jun 02 '20 at 01:25
  • 1
    `String`s are already heap-allocated and cheap to move. There's no need to add a level of indirection with `&String`. – John Kugelman Jun 02 '20 at 01:32
  • 1
    Rust does not have an `undefined` like JavaScript. You can't have a `Vec` with "holes" in it. Perhaps [Should I store unordered values from a series with large holes in a sparse Vec or HashMap in Rust?](https://stackoverflow.com/q/59948389/155423) would answer your question. You might just want a `HashMap`. – Shepmaster Jun 02 '20 at 01:34

2 Answers2

7

When you do insert + remove (or the double splice in JavaScript) you move all of the items between the larger of the two indices and the end of the array twice: first you move them back one slot for the remove, and then you move them forward one slot for the insert. But this is unnecessary. Instead you can simply take a slice of the Vec and rotate it:

fn move_me(arr: &mut [String], old_index: usize, new_index: usize) {
    if old_index < new_index {
        arr[old_index..=new_index].rotate_left(1);
    } else {
        arr[new_index..=old_index].rotate_right(1);
    }
}

Note that this change allows move_me to take &mut [String] instead of &mut Vec<String>, which makes this code more general as well as more efficient. It is better to accept &[T] instead of &Vec<T>, and in this case the same logic applies to &mut Vec<T> because move_me does not need to grow or shrink the vector.

Also, as in the other answer, I have left out the part that makes negative indices count from the back of the slice, and the part that grows the vector when the index is too large, because neither of those conventions is common in idiomatic Rust.

trent
  • 25,033
  • 7
  • 51
  • 90
-2

Thanks to SCappella for telling me that JavaScript Array.splice() does the same thing as Rust Vec.insert() and Vec.remove(). So I just went ahead and ported the function as literally as I could.

Thanks to John Kugelman for letting me know I can delete everything but the last 2 lines.

/* move is a reserved identifier */
fn move_(arr: &mut Vec<String>, old_index: usize, new_index: usize) {
    let removed = arr.remove(old_index);
    arr.insert(new_index, removed);
}
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
GirkovArpa
  • 4,427
  • 4
  • 14
  • 43
  • 3
    Handling negative and out of range indices doesn't need to be ported over. Mutating the index parameters is poor style. I'd ditch all that (everything but the last two lines, really). – John Kugelman Jun 02 '20 at 02:31