为什么在Rust中允许返回当前函数拥有的引用?

nyb*_*bon 3 struct initialization lifetime ownership rust

我正在学习Rust的寿命/所有权概念,并想解释一下Rust(rustc 1.37.0)中的以下行为。

对于这样的程序:

#[derive(Debug)]
struct Book {
    price: i32,
}

fn main() {
    let book1 = Book {price: 12};
    let cheaper_book = choose_cheaper(&book1);
    println!("{:?}", cheaper_book);
}

fn choose_cheaper(b1: &Book) -> &Book {
    if b1.price < 15 {
        b1
    } else {
        let cheapest_book = Book {price: 0};
        &cheapest_book
    }
}
Run Code Online (Sandbox Code Playgroud)

Rust报告:

17 |   &cheapest_book
   |   ^^^^^^^^^^^^^^ returns a reference to data owned by the current function
Run Code Online (Sandbox Code Playgroud)

而且我可以理解该错误,这是因为变量cheapest_book是价格为0的Book的所有者,并且将在此函数的结尾将其删除,因此之后返回的引用将变为无效。但是,我很难解释为什么将choose_cheaper函数更改为以下内容时可以使用以下内容:

17 |   &cheapest_book
   |   ^^^^^^^^^^^^^^ returns a reference to data owned by the current function
Run Code Online (Sandbox Code Playgroud)

可以给我一些启示吗?谢谢。

小智 7

在线路let cheapest_book = &Book {price: 0};中,Book不是在一个“新”的实例Book类型。每次调用此函数时,它将返回对该类型相同实例的引用,该引用Book将存储在可执行文件的只读数据部分中(或者,如果包含a CellAtomicUsize类似内容,则从技术上讲,存储在该数据部分中) 。

在这种情况下,我们可以将代码“扩展”为更明确的内容:

static GLOBAL_BOOK: Book = Book { price: 0 };

fn choose_cheaper<'a>(b1: &'a Book) -> &'a Book {
    if b1.price < 15 {
        b1
    } else {
        let cheapest_book = &GLOBAL_BOOK;
        cheapest_book
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,对的引用GLOBAL_BOOK实际上可能是&'static Book,但是&'a Book是该引用的超类型,因此可以将静态引用当作引用一样返回'a

如果这看起来很奇怪,请考虑一下这正是字符串文字所发生的;它们只是没有显式的&字符:在之后let foo = "string!";foo&'static str对可执行文件的只读部分中的某些数据的引用,而不是本地对象。因此,您还可以编写return "string!";返回&'a strany的函数'a

生锈是否进行此转换的规则是您“构造”一个​​对象时(使用元组语法,结构或枚举或联合初始化语法,数字或字符串文字或它们的任意组合,而不是函数调用new()或任何其他函数) )&,它们将成为匿名静态对象。因此,实际上&&1_u32'static对static的'static引用u32