Vladimir Matveev
Vladimir Matveev

Reputation: 127771

Using other methods on a structure inside a `for` loop over an iterator which can mutate this structure

I have something like the following methods on a structure:

impl<'a> SomeStructure<'a> {
    // I need &'a mut because the iterator may mutate SomeStructure
    fn iter<'a>(&'a mut self) -> SomeIterator<'a> {
        SomeIterator { object: self }
    }

    fn test_something(&self) -> bool {
        self.some_field < 0
    }

}

And then I want to use them like that:

impl<'a> SomeTrait for &'a mut SomeStructure<'a> {
    fn do_something(self) {
        for e in self.iter() {
            ...
            if self.test_something() {
                break;
            }
        }
    }
}

However, Rust does not allow it (I've fixed error messages so they refer to the sample code above):

io/convert_io.rs:119:17: 119:22 error: cannot borrow `*self` as immutable because it is also borrowed as mutable
io/convert_io.rs:119             if self.test_something() {
                                    ^~~~~
io/convert_io.rs:117:18: 117:23 note: previous borrow of `*self` occurs here
io/convert_io.rs:117         for e in self.iter() {
                                      ^~~~~

But I do not see how immutable borrowing inside self.test_something() call can interfere with creating an iterator earlier, even if the iterator does mutate original object.

Could you please explain what's going on here and how to fix it?

Upvotes: 3

Views: 214

Answers (1)

huon
huon

Reputation: 102066

This is possibly bug #8372 caused by the minimum-that-works implementation of the current for. It is implemented as a macro such that

for pattern in iterator { body }

expands to (you can see this by running rustc --pretty expanded foo.rs)

{
    let it = &mut iterator;
    loop {
        match it.next() {
            None => break,
            Some(pattern) => { body }
        }
    }
}

The problem is the &mut iterator borrow, which stops iterator being used directly while it is in scope. You can normally work around this by manually writing the expansion yourself:

impl<'a> SomeTrait for &'a mut SomeStructure<'a> {
    fn do_something(self) {
        let mut it = self.iter();
        loop {
            match it.next() {
                None => break
                Some(e) => {
                    if self.test_something() {
                        break;
                    }
                }
            }
        }
    }
}

That said... this may not actually work in this case if self.iter() borrows self (especially if test_something takes &mut self, since the compiler has to disallow mutation or the iterator could be invalidated).

If you were to add a .view_creator() method to the return of self.iter() (assuming you have a reference to self in the self.iter() type) then it.view_creator().test_something() would work (with the manually unwrapped for loop).

(FWIW, it's a little peculiar to have a trait taking self and implementing it on &mut Thing rather than taking &mut self and implementing it straight on Thing; although there are good reasons for it sometimes.)

Upvotes: 2

Related Questions