Please tell me, guys, why it can be compiled here

#[derive(Debug)]
struct Thing {
    count: u32,
}
fn test() -> &'static Thing {
    let a = &Thing { count: 2 };
    a
}

fn main() {
    let t = test();
    println!("{:?}", t);
}

There are a few doubts:

  1. When the scope of the test function ends, why can the reference be returned.

  2. From this example, it seems that the Thing struct in the test function is not allocated on the stack. Isn't the struct in rust allocated on the stack?

thanks for your answer

  1. Your reference's lifetime is specified to be 'static, which means "may live until the end of the program". Thus there is no issue with returning it. You're not returning a reference to a local variable, but rather a reference to some global (static, in this case) data.

  2. This is called static promotion. Basically, it's the syntax sugar for

fn test() -> &'static Thing {
    static TEST: &Thing = Thing { count: 2};
    let a = &TEST;
    a
}

The exact rules what is and isn't promoted are a bit obscure, but generally if it looks like a simple constant expression, then it can. If it can't, you'll just get a compile error.

6 Likes

I understand, thank you very much for your answer!

Change the function to

fn test() -> &'static Thing {
    let b = Thing { count: 2 };
    let a = &b;
    a
}

and you'll get the error you were expecting: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d6b9da02728797cfae53fe34ea102610

error[E0515]: cannot return value referencing local variable `b`
 --> src/main.rs:8:5
  |
7 |     let a = &b;
  |             -- `b` is borrowed here
8 |     a
  |     ^ returns a value referencing data owned by the current function

You just happened to trip a special case that exists for convenience.


Aside: I strongly suggest using String for explorations like this, because simple Copy primitives like u32 have a bunch of special exemptions.

Change Thing::count to a String, and then it again fails:

error[E0515]: cannot return value referencing temporary value
 --> src/main.rs:7:5
  |
6 |     let a = &Thing { count: "lots".to_string() };
  |              ----------------------------------- temporary value created here
7 |     a
  |     ^ returns a value referencing data owned by the current function

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

3 Likes

Thank you for the answer, that helps to clear it up.

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.