8

Consider a simple selection sort on a &mut Vec<&mut String>:

fn selection_sort(collection: &mut Vec<&mut String>) {
    for i in 0..collection.len() {
        let mut least_element = i;
        for j in (i + 1)..collection.len() {
            if collection[j] < collection[least_element] {
                least_element = j;
            }
        }

        collection.swap(least_element, i);
    }
}

This loop should work, based on this and that – yet the borrow throws this error:

error[E0596]: cannot borrow data in a `&` reference as mutable
  --> src/main.rs:58:28
   |
58 |             if chunks[j] < chunks[least_element] {
   |                            ^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
   |
   = help: trait `IndexMut` is required to modify indexed content

Or in newer versions of Rust:

error[E0596]: cannot borrow data in an index of `std::vec::Vec<&mut std::string::String>` as mutable
 --> src/lib.rs:5:32
  |
5 |             if collection[j] < collection[least_element] {
  |                                ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
  |
  = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `std::vec::Vec<&mut std::string::String>`

Wouldn't it make more sense to have a & reference be mutable?

The IndexMut documentation doesn't use an example I understand well and has a pretty large example that doesn't seem to clearly demonstrate how to use IndexMut, especially in the context of a selection sort, or swapping elements.

Error 0596 explains it occurs when trying to borrow from an immutable value, yet least_element is mutable. If i is changed to mut i this also does compile (and the compiler recommends removing mut from i).

Is there a Rustacean who can illuminate this?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
NonCreature0714
  • 5,744
  • 10
  • 30
  • 52
  • 1
    I think that the compiler is going crazy trying to decide whether to use `Index` or `IndexMut` while deciding the override of `PartialEq::lt` to use. You can workaround with a couple of temporary variables such as `let a = &collection[j]; let b = &collection[least_element]; if a < b ...` – rodrigo Sep 06 '19 at 21:41
  • 1
    Or you can `use std::ops::Index;` and write `if collection.index(j) < collection.index[least_element] ...` – rodrigo Sep 06 '19 at 21:44
  • 1
    Or simply `if collection[j].lt(&collection[least_element]) ...`. I'm starting to think that you may have found a compiler bug... – rodrigo Sep 06 '19 at 21:47
  • 1
    [`&collection[j] < &collection[least_element]`](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f554107d08ff7e0ff09b96b37f87a951) also works. Don't know why though. – mcarton Sep 06 '19 at 22:01
  • 1
    So does [`*collection[j] < *collection[least_element]`](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6c66ed3b946e73005b1f1e241ad54d48) – mcarton Sep 06 '19 at 22:02
  • 1
    [Why can I not call borrow_mut() after indexing into an immutable Vec?](https://stackoverflow.com/questions/52989901/why-can-i-not-call-borrow-mut-after-indexing-into-an-immutable-vecrefcell) seems to be about the same issue, although it's less clear to me why your example doesn't work. The only thing they obviously have in common is the incorrect choice of `IndexMut` where `Index` would suffice. – trent Sep 06 '19 at 23:00
  • 1
    Couldn't find an existing bug, so I filed this [ticket](https://github.com/rust-lang/rust/issues/64261). – edwardw Sep 07 '19 at 16:38
  • @edwardw Thanks for filing the issue :) I went to do it but couldn't get the right words for it. – NonCreature0714 Sep 07 '19 at 21:13

1 Answers1

2

When you try to access collection[j], the compiler returns a &mut String because that's the type of the vector's elements. When you try to access collection[least_element], the borrow checker doesn't know if least_element != j, and having two mutable references of the same element would be undefined behavior. You can either use std::ops::Index which returns a &&mut String (and it's safe to have two immutable references to the same mutable reference), directly borrowing the elements (&collection[j] < &collection[least_element]) or, if possible, changing the type of collection to Vec<&String> or Vec<String>.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366