1

i have code below,

    enum List<'a> {
        Cons(i32, Box<&'a List<'a>>),
        Nil,
    };
    let list= Cons(10, Box::new(&Nil));
    let lista = Cons(5, Box::new(&Cons(10, Box::new(&Nil))));
    match lista {
        Cons(x,_) => println!("{}",x),
        Nil => ()
    }

after running the code, the compiler says

21 |     let lista = Cons(5, Box::new(&Cons(10, Box::new(&Nil))));
   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^  - temporary value is freed at the end of this statement 
   |                                   |
   |                                   creates a temporary which is freed while still in use

i can understand 'Cons(10, Box::new(&Nil))' is freed, so it goes wrong. but when i replace list with lista after match, eg:

    match list {
        Cons(x,_) => println!("{}",x),
        Nil => ()
    }

i think Nil is also a temporary value and dropped at the end of statement, but it runs well, what's the difference between list and lista?

MichealRay
  • 43
  • 6
  • I think `Box<&…>` is generally meaningless. Use `Box` (or `&'a List<'a>` if you want [on-stack](https://rust-unofficial.github.io/too-many-lists/infinity-stack-allocated.html) or bump-allocated lists.) In any case, have a look at the [Too Many Lists](https://rust-unofficial.github.io/too-many-lists/index.html) book, if that's not what you're doing already. – Caesar Feb 02 '23 at 06:27

1 Answers1

1

Its because of rvaue static promotion. Enum variants with all const values are constexpr, and therefore are promoted to a 'static lifetime.

For example:

enum TestEnum {
    Dynamic(String),
    NoValue,
    ConstStr(&'static str)
}
fn main () {
    use TestEnum::*;
    println!("NoValue:   {:?}", &NoValue as *const TestEnum);
    println!("NoValue:   {:?}", &NoValue as *const TestEnum);
    println!("ConstStr:  {:?}", &ConstStr("const-str") as *const TestEnum);
    println!("ConstStr:  {:?}", &ConstStr("const-str") as *const TestEnum);
    println!("Dynamic:   {:?}", &Dynamic(String::from("dynamic")) as *const TestEnum);
    println!("Dynamic:   {:?}", &Dynamic(String::from("dynamic")) as *const TestEnum);
}

outputs:

NoValue:   0x7ff6a92cf380
NoValue:   0x7ff6a92cf380
ConstStr:  0x7ff6a92cf3e8
ConstStr:  0x7ff6a92cf3e8
Dynamic:   0xd56df7f578
Dynamic:   0xd56df7f5f8

Only the Dynamic variant is stored in a new address on creation. You can also see this with explicit lifetimes:

// Compiles fine
let static_vaue : &'static TestEnum = &ConstStr("const-str"); 

// Does not compile
let dynamic_value : &'static TestEnum = &Dynamic(String::from("const-str")); 

So the reason your code works when using Nil is because the Nil value being referenced does not get dropped after the statement as it is living in 'static memory, so it will never be dropped.

pigeonhands
  • 3,066
  • 15
  • 26