Borrow Checker Used to Check This Code

Full code at: https://github.com/brunoczim/lcalc

Consider this code (it is a beta-reduction function):

    pub fn replace(&mut self, name: &Rc<str>, val: &Self) {
        let mut stack = Vec::with_capacity(8);
        stack.push(self);
        while let Some(refer) = stack.pop() {
            match refer {
                Var(sym) => match sym {
                    Dyn(s) => {
                        if &**s == &**name {
                            *refer = val.clone();
                        }
                    },
                    Static(s) => {
                        if Rc::ptr_eq(s, name) {
                            *refer = val.clone();
                        }
                    },
                },
                App(fun, arg) => {
                    stack.reserve(2);
                    stack.push(fun);
                    stack.push(arg);
                },
                Lambda(_, body) => {
                    stack.push(body);
                },
            }
        }
    }

I first wrote this code in April, and it did use to compile. However, if a try to compile it with a recent compiler, I get this error:

error[E0506]: cannot assign to `*refer` because it is borrowed                                                                                                           
   --> src/eval.rs:108:29                                                                                                                                                
    |                                                                                                                                                                    
105 |                 Var(sym) => match sym {                                                                                                                            
    |                     --- borrow of `*refer` occurs here                                                                                                             
...                                                                                                                                                                      
108 |                             *refer = val.clone();                                                                                                                  
    |                             ^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `*refer` occurs here                                                                       
                                                                                                                                                                         
error[E0506]: cannot assign to `*refer` because it is borrowed                                                                                                           
   --> src/eval.rs:113:29                                                                                                                                                
    |                                                                                                                                                                    
105 |                 Var(sym) => match sym {                                                                                                                            
    |                     --- borrow of `*refer` occurs here                                                                                                             
...                                                                                                                                                                      
113 |                             *refer = val.clone();                                                                                                                  
    |                             ^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `*refer` occurs here                                                                       
                                                                                                                                                                         
error: aborting due to 2 previous errors 

I found that weird, so I tried to compile it with a April built Rust compiler.
cargo +nightly-2018-04-30 build
And it works!

Why is this happening? Isn't this a bad breaking change? And how can I fix my nowadays code? It seems impossible.

Unless in worked in an old stable then would not be counted as breaking.
Fix is to do the replacement outside of the match and just record that is needs doing beforehand.

How do I install an old stable? I remember it used to compile with old stable, but I don't know how to install.

Your code incorrectly compiled due to a bug introduced in rust 1.26.0. The bug was fixed in 1.26.2.
Announcing Rust 1.26.2 | Rust Blog

Not compiling is the correct behavior.

2 Likes

I see... Rust 1.26.0 is the exact version that compiled my code. I have installed it right now.

Solved it. I had to defer the replacement and insert some ugly continues, but solved it.

    pub fn replace(&mut self, name: &Rc<str>, val: &Self) {
        let mut stack = Vec::with_capacity(8);
        stack.push(self);
        while let Some(refer) = stack.pop() {
            let shall_replace = match refer {
                Var(sym) => match sym {
                    Dyn(s) => &**s == &**name,

                    Static(s) => Rc::ptr_eq(s, name),
                },

                App(fun, arg) => {
                    stack.reserve(2);
                    stack.push(fun);
                    stack.push(arg);
                    continue;
                },

                Lambda(_, body) => {
                    stack.push(body);
                    continue;
                },
            };

            if shall_replace {
                *refer = val.clone()
            }
        }
    }