Home > Mobile >  Extend a string with another string within the same vector Rust
Extend a string with another string within the same vector Rust

Time:11-28

I am new to Rust and I cannot solve this simple problem: Consider following code:

let mut a = vec![String::from("aa"), String::from("bb")];
a[0]  = &*a[1];

Borrow checker rightfully complains about me having both immutable and mutable borrows here. It also suggests me:

help: try adding a local storing this...
  --> src\main.rs:61:15
   |
61 |     a[0]  = &*a[1];
   |               ^^^^
help: ...and then using that local here
  --> src\main.rs:61:5
   |
61 |     a[0]  = &*a[1];
   |     ^^^^^^^^^^^^^^

I do not really understand what that means. Do I really need to clone the string to perform such an easy operation? (That would result in 2 overall copies: into temporary and then back into a[0], instead of optimal 1 copy straight into a[0])

CodePudding user response:

Naively, like this (no reason to dereference a[1]):

fn main() {
    let mut a = vec![String::from("aa"), String::from("bb")];
    a[0]  = &a[1];
}

Sadly, as you noticed, this creates a problem with the borrow checker. Modifying a[0] borrows it mutably, while you also need to borrow a[1] immutably to append it. And borrowing a both mutably and immutably at the same time isn't allowed:

error[E0502]: cannot borrow `a` as immutable because it is also borrowed as mutable
 --> src/main.rs:3:14
  |
3 |     a[0]  = &a[1];
  |     ---------^---
  |     |        |
  |     |        immutable borrow occurs here
  |     mutable borrow occurs here
  |     mutable borrow later used here

The reason is that Rust doesn't know that a[0] and a[1] are independent variables. This behavior is intentional and important, as it prevents some cases that could lead to undefined behavior. Rust has a zero-undefined-behavior guarantee.

You can split the array into two independently mutable parts via the split_at_mut function, though. This works because Rust can now guarantee that both parts are independent. Borrowing them sequentially makes it impossible for (current) Rust to prove that.

fn main() {
    let mut a = vec![String::from("aa"), String::from("bb")];

    let (a_left, a_right) = a.split_at_mut(1);
    a_left[0]  = &a_right[0];

    println!("{:?}", a);
}
["aabb", "bb"]

Note that the split_at_mut is almost free. It doesn't actually copy anything, it just creates two slices that point at two non-overlapping parts of the array. It doesn't cause any performance overhead.

  • Related