Why does this simple program not compile?

Hello,

I'm quite new to Rust and I've got this very simple program that is not compiling but I can't seem to work out why:

fn main() {
    let mut var52: i32 = 919130359i32;
    let mut var51: &mut i32 = &mut var52;
    let var50: &mut &mut i32 = &mut var51;
    let var49: &&mut &mut i32 = &var50;
    {
        let mut var231: i32 = 1735471491i32;
        let mut var230: &mut i32 = &mut var231;
        let var229: &mut &mut i32 = &mut var230;
        let mut var228: &&mut &mut i32 = &var229;
        var228 = &var50;
    }
    println!("{:?}", var49);
}

The error it produces is:

error[E0597]: `var231` does not live long enough
  --> test.rs:8:36
   |
8  |         let mut var230: &mut i32 = &mut var231;
   |                                    ^^^^^^^^^^^ borrowed value does not live long enough
...
12 |     }
   |     - `var231` dropped here while still borrowed
13 |     println!("{:?}", var49);
   |                      ----- borrow later used here

Could anyone else explain what is going wrong? Thanks very much!

(PS I'm aware isn't a very useful program in practice, but this was generated from a Rust fuzzer I am working on which is meant to randomly produce valid Rust programs to test the Rust compiler GitHub - rustsmith/rustsmith: A randomized program fuzzer for the Rust programming language. Based on my knowledge this should have been valid Rust code but I'm just trying to work out why the compiler throws an error!)

Let var50 have the type &mut &'a mut i32, and let var229 have the type &mut &'b mut i32. When we assign &mut var51 to var50, we force 'a to live no longer than var52. When we assign &mut var230 to var229, we force 'b to live no longer than var231. Finally, when we assign &var50 to var228, we force 'a and 'b to be exactly the same, since lifetimes under &mut references are invariant (that is, the 'a in &var50 cannot be made any shorter).

Now, since a reference to a value cannot live any longer than the original, none of the references can outlive var231. In particular, var49 has type &&mut &'a mut i32, so it cannot outlive 'a, which is equal to 'b, which cannot outlive var231. This results in the borrow checker error.

3 Likes

Let's say that var50 is a &'b mut &'a mut _ and that var228 is a &'f &'e mut &'d mut _.

'a and 'd are behind another &mut so they cannnot change (they are invariant). So the assignment of &var50 to var228 means that 'a and 'd are in fact the same.

But 'd cannot live longer than var231 (i.e. the inner block), whereas 'a must last until the print!. Hence the error.


I don't expect a beginner to follow most of that. Your program conflates borrows of the inner block with borrows outside that block in unresolvable ways.

1 Like

As far as I understand it, it is not so easy to get not confused by variables, what's behind variables and what's behind references (or references to references to........).
I think that the reference to var50 is assigned to var228 (you will correct me if I am wrong, and I think that I sometimes use an improvable terminology..). Before leaving the {} block (statement), so when everything inside goes out of scope, RUST "cleans up" everything that's inside the statement and frees the memory. It can only clean up from bottom to top and would need to free the memory that lies behind the reference to var50. &var50 cannot be freed (dropped) because it is intended to be used in the println macro where var49 also holds a reference to what is behind var50. But now your var231 cannot be dropped and the procedure cannot finish. You create a "frustrated system by doing so". (With an integer instead of a reference this wouldn't happen because the integer would be copied not moved (or so).)

Here a citation from: Rust for Rustaceans, page 9 (I think I am allowed to cite it)
DROP ORDER
Rust automatically drops values when they go out of scope [...]. The rules for the order in which to drop are fairly simple: variables (including function arguments) are dropped in reverse
order, and nested values are dropped in source-code order.
This might sound weird at first—why the discrepancy? If we look at it
closely, though, it makes a lot of sense. Say you write a function that declares
a string and then inserts a reference to that string into a new hash table. When
the function returns, the hash table must be dropped first; if the string were
dropped first, the hash table would then hold an invalid reference! In general,
later variables may contain references to earlier values, whereas the inverse
cannot happen due to Rust’s lifetime rules. And for that reason, Rust drops variables
in reverse order.
Now, we could have the same behavior for nested values, like the values
in a tuple, array, or struct, but that would likely surprise users. If you constructed
an array that contained two values, it’d seem odd if the last element of the array
were dropped first. The same applies to tuples and structs, where the most intuitive
behavior is for the first tuple element or field to be dropped first, then the
second, and so on. Unlike for variables, there is no need to reverse the drop
order in this case, since Rust doesn’t (currently) allow self-references in a single
value. So, Rust goes with the intuitive option.

Here a code that would work with some comments when it would not work:

//#[allow(dead_code, unused_variables, unused_mut)]
fn main() {
    let mut a: i32 = 9;
    a = 10;
    let b = &mut a;
    *b = 11;
    drop(b);
    // must drop b otherwise problem because a cannot be
    //borrowed mutably (from b) and unmutably (for printing)
    println!("{a}");

    let mut vart: i32 = 45;
    let vart1 = &&mut &mut vart;
    {
        let mut var231: i32 = 1735471491i32;
        let mut var230: &mut i32 = &mut var231;
        let var229: &mut &mut i32 = &mut var230;
        let mut var228: &&mut &mut i32 = &var229;
        // this reference leads to an unsatisfiable condition 
        //var228 = &vart1;
    }
    println!("{:?}", vart1);
    
    // two other examples concerning borrowing:
    
    /*
    //this would work
    println!("{:?}", vart1);
    println!("{:?}", vart);
    */
    
    /*
    //this would NOT work
    println!("{:?}", vart);
    println!("{:?}", vart1);
    */
}

See also here and the links in the answer ( Do mutable references have move semantics?)

I don't think this is an accurate description. var50 is not necessarily freed at the end of the block, just because we took a reference to it. Indeed, if we remove the = &var229, the program compiles and runs (Rust Playground):

fn main() {
    let mut var52: i32 = 919130359i32;
    let mut var51: &mut i32 = &mut var52;
    let var50: &mut &mut i32 = &mut var51;
    let var49: &&mut &mut i32 = &var50;
    {
        let mut var231: i32 = 1735471491i32;
        let mut var230: &mut i32 = &mut var231;
        let var229: &mut &mut i32 = &mut var230;
        let mut var228: &&mut &mut i32 /* = &var229 */;
        var228 = &var50;
    }
    println!("{:?}", var49); // 919130359
}

The real issue comes from variance: &var229 and &var50 are constrained to have the same &mut i32 lifetime, and this lifetime cannot extend beyond the inner block. Thus, the &var50 contained in var49 cannot be accessed by the println!().

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.