Repository: rust
Issue: Weird borrowck issues with &mut &mut [u8]
Labels: A-borrow-checker,T-compiler,C-bug

I ran into some bizarre borrowck issues when trying to write a function that would modify a `&mut &mut [u8]`. The goal here was to make it simple to shove data into a `[u8, ..512]` by virtue of copying data into the corresponding `&mut [u8]` and then modifying the slice. I tried three different approaches, and they all had issues. The various errors are commented into the source.

(All 3 approaches require `use std::vdc::MutableCloneableVector`)

The first approach tried to define a function `append(buf: &mut &mut [u8], v: &[u8])` that would copy the data into `buf` and then modify `*buf` to contain the new slice. This ran into an odd lifetime error where it thinks that I can't say `*buf = buf.mut_slice_from(len)`. It claims the lifetime of `buf` is too short, but I don't see why that matters. I'm actually re-slicing `*buf`, and putting the result back into the same location that held the original slice, so I would expect it to have the same lifetime and, therefore, be valid.

``` Rust
fn one() {
    let mut line = [0u8, ..512];
    let mut buf = line.as_mut_slice();

    fn append(buf: &mut &mut [u8], v: &[u8]) {
        let len = buf.clone_from_slice(v);
        *buf = buf.slice_from_mut(len);
// error: lifetime of `buf` is too short to guarantee its contents can be safely reborrowed
//             ^~~
// note: `buf` would have to be valid for the anonymous lifetime #2 defined on the block at 7:45...
// note: ...but `buf` is only valid for the anonymous lifetime #1 defined on the block at 7:45
    }

    append(&mut buf, b"test");
    append(&mut buf, b"foo");
}
```

The second approach was to give both levels of indirection the same lifetime, e.g. `append<'a>(&'a mut &'a mut [u8], v: &[u8])` to try and squelch the error. This didn't work because I wasn't allowed to reassign back to `*buf`, as it considered `buf.mut_slice_from(len)` to borrow it. I assume the borrow check is tied to the lifetime, which is shared at both levels, so borrowck thinks `buf` is borrowed when it's really `*buf` that's borrowed.

Curiously, it also decided I couldn't use `&mut buf` twice in the calling code, as it seemed to think it was already borrowed.

``` Rust
fn two() {
    let mut line = [0u8, ..512];
    let mut buf = line.as_mut_slice();

    fn append<'a>(buf: &'a mut &'a mut [u8], v: &[u8]) {
        let len = buf.copy_from(v);
        *buf = buf.mut_slice_from(len);
// error: cannot assign to `*buf` because it is borrowed
//      ^~~~
// note: borrow of `*buf` occurs here
//             ^~~
    }

    append(&mut buf, bytes!("test"));
    append(&mut buf, bytes!("foo"))
// error: cannot borrow `buf` as mutable more than once at a time
//         ^~~~~~~~
// note: previous borrow of `buf` as mutable occurs here
//         ^~~~~~~~
}
```

The third approach was to ditch `&mut &mut [u8]` entirely and try capturing `buf` in a closure instead. This gave some odd errors. First off, it kept referencing `(*buf)[]`, and I don't know what it meant by that. Also, the first error here indicates that a borrow on a _later_ line was blocking a borrow on an _earlier_ line, which is quite bizarre. How can `buf` have been borrowed already when the later line is, well, later? It also considered the same reference to `buf` to consist of multiple mutable borrows.

``` Rust
fn three() {
    let mut line = [0u8, ..512];
    let mut buf = line.as_mut_slice();

    let append = |v: &[u8]| {
        let len = buf.copy_from(v);
// error: cannot borrow `(*buf)[]` as mutable more than once at a time
//                ^~~
        buf = buf.mut_slice_from(len);
// note: previous borrow of `(*buf)[]` as mutable occurs here
//            ^~~
// error: cannot borrow `(*buf)[]` as mutable more than once at a time
//            ^~~
// note: previous borrow of `(*buf)[]` as mutable occurs here
//            ^~~
// error: cannot assign to `buf` because it is borrowed
//      ^~~
// note: borrow of `buf` occurs here
//            ^~~
    };

    append(bytes!("test"));
    append(bytes!("foo"));
}
```

In the end, I couldn't figure out any way to accomplish what I wanted. It seems to me the first approach should have worked.

/cc @nikomatsakis