Cannot borrow as mutable more than once

Hi all, sorry to bore you with another amateur borrow checker question. The code is simple:

Playground link

#![feature(nll)]

fn foo<'a, T>(_i: &'a mut T)
where
    T: Iterator<Item = &'a str>,
{
}


fn main() {
    let s = "hello world".to_owned();
    let mut split = s.split(' ');

    foo(&mut split); // line 14
    foo(&mut split); // line 15

}
error[E0499]: cannot borrow `split` as mutable more than once at a time
  --> src/main.rs:15:9
   |
14 |     foo(&mut split); // line 14
   |         ---------- first mutable borrow occurs here
15 |     foo(&mut split); // line 15
   |         ^^^^^^^^^^
   |         |
   |         second mutable borrow occurs here
   |         borrow later used here

I'm afraid I don't understand why the &mut split borrow on line 14 is still active when we get to line 15. At first I thought that NLL isn't handling this case, so I put line 14 and line 15 each into their own block and named the references, but it didn't help:

fn main() {
    let s = "hello world".to_owned();
    let mut split = s.split(' ');

    {
        let r1 = &mut split;
        foo(r1);
    }
    {
        let r2 = &mut split;
        foo(r2);
    }
}

This code is so simple, I must be missing something obvious, but I cannot see it. Where am I going wrong?

Thanks

Because we didn't drop split yet. If you want to reuse it, you can return it.

The compiler sees you passing an &'a mut Split<'a, P> into foo because Split<'a, P>: Iterator<Item=&'a str>. It has no way of knowing that foo doesn't store a borrow of the Split back into itself, since it has to treat it as (more-or-less, barring variance analysis) opaque, and the type specifies that it can store references with its own lifetime and is mutable. If the borrow might escape into itself, then it has to extend until the borrowed value goes out of scope.

It works if you allow it to be &'a mut Split<'b, P> (Playground):

fn foo<'a, 'b, T>(_i: &'a mut T)
where
    T: Iterator<Item = &'b str>,
    'b: 'a,
{
}

fn main() {
    let s = "hello world".to_owned();
    let mut split = s.split(' ');

    foo(&mut split);
    foo(&mut split);
}
2 Likes

It also works if you don't explicitly specify the lifetime of the parameter of foo()

fn foo<'a,T>(_i: &mut T)
where
    T: Iterator<Item = &'a str>,
{
}

(Playground)

1 Like

Others have explained the borrow issue already, but you may want to consider a signature like this instead:

fn foo<'a, T>(_i: T)
where
    T: IntoIterator<Item = &'a str>,
{
}

This is more flexible and ergonomic for callers. They can move a value into this function if they don’t care about the value anymore, or pass a reference if they do (as in your example).

This also avoids running into the issue you hit because you’re not specifying the lifetime of the iterator borrow, and thus can’t accidentally specify a too-long one.

2 Likes