Two mutable references in the iterator and peekable

Hello,

recently I encountered this example

#[test]
fn foo() {
        let s = "abcd".to_string();
        let mut it: Chars = s.chars();
        let r_it: &mut Chars = &mut it;
        let mut peek: Peekable<&mut Chars> = r_it.peekable();
        assert_eq!(peek.next(), Some('a'));
        assert_eq!(r_it.next(), Some('b'));
        assert_eq!(it.next(), Some('c'));
    }

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f57d994759ffeb8699eb21d961349c35

and I wonder why is it possible? Typically I would assume that only one mutable reference can exist.

After this in analogy with an Iterator trait I tried to implement the following that might leak a reference

trait Leakable {
        fn leak(self) -> Leak<Self>
        where
            Self: Sized,
        {
            Leak::new(self)
        }

        fn change(&mut self);
    }

    #[derive(Clone)]
    struct Leak<T: Leakable> {
        val: T,
    }

    impl<T: Leakable> Leak<T> {
        fn new(val: T) -> Leak<T> {
            Leak { val }
        }
    }

    impl<T: Leakable> Leakable for Leak<T> {
        fn change(&mut self) {
            self.val.change()
        }
    }

    #[derive(Debug)]
    struct Bar {
        val: i32,
    }

    impl Leakable for Bar {
        fn change(&mut self) {
            self.val += 1;
        }
    }

    #[test]
    fn bar() {
        let mut i: i32 = 5;
        let mut bar: Bar = Bar { val: i };
        let r_bar: &mut Bar = &mut bar;
        let mut leak: Leak<Bar> = r_bar.leak();
        leak.change();
        assert_eq!(leak.val.val, 6);
        assert_eq!(r_bar.val, 6);
        assert_eq!(bar.val, 6);
    }

However in contrast with the iterator the leak in bar() has a type of Leak<Bar> and not Leak<&mut Bar> as with the iterator example, where peek has a type peek: Peekable<&mut Chars>. And here is my second question why is the type of leak different from the type of peek?

Accordingly as I would expect due to the type Leak<Bar> the code fails to compile with the error

error[E0507]: cannot move out of `*r_bar` which is behind a mutable reference
   --> test.rs:590:35
    |
590 |         let mut leak: Leak<Bar> = r_bar.leak();
    |                                   ^^^^^^^^^^^^ move occurs because `*r_bar` has type `Bar`, which does not implement the `Copy` trait

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c030c8a1bcefbcf49c261a48fc88fe2e

Can anybody explain me what is happening here.

Thanks.

A mutable reference which is being reborrowed from is "freezed", you can't access it as long as the reborrow is alive.

In the first code snippet you posted, you happen to be "lucky" and you call the methods in exactly the opposite order in which their corresponding iterators have been created. This allows NLL to kick in and end the borrow to peek first, which makes it possible to use r_it, and then the borrow of r_it ends too, which makes it possible to use it itself. If you shuffle around the three next() calls in any other way, it won't compile anymore.

3 Likes

Ok, thanks for the explanation. I had to be really lucky in that case:-)))

Maybe by a chance you also know why in the other snippet the type of leak is different for the type of peek?

&mut Bar doesn't implement Leakable so autoderef kicks in and sees that Bar: Leakable, so it tries that (only) remaining possibility. If you impl Leakable for &'_ mut Bar, the code compiles.

(In contrast, &mut Iterator is also an Iterator due to the existing blanket impl in the standard library.)

Thanks a lot. This is what I was also suspecting, but I was confused by the fact that I did not find the implementation for &mut Iterator in the std. (Maybe I just overlooked it.)

Here it is.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.