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

时间:2019-09-06 03:09:57

标签: struct rust initialization lifetime ownership

我正在学习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
    }
}

铁锈报告

17 |   &cheapest_book
   |   ^^^^^^^^^^^^^^ returns a reference to data owned by the current function

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

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

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

1 个答案:

答案 0 :(得分:7)

let cheapest_book = &Book {price: 0};行中,Book不是{em>不是 Book类型的“新”实例。每次调用此函数时,它都会返回对Book类型的 same 实例的引用,该实例将存储在可执行文件的只读数据部分中(或者从技术上讲,数据节中是否包含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
    }
}

请注意,对GLOBAL_BOOK的引用实际上可能是&'static Book,但是&'a Book是该引用的超类型,因此可以将静态引用当作{{1 }}参考。

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

是否将进行转换的规则是何时“构造”对象(使用元组语法,结构或枚举或联合初始化语法,数字或字符串文字或它们的任意组合-不是< / strong>函数调用'a后面的new()或任何其他函数),它们将成为匿名静态变量。因此,实际上&是对静态&&1_u32的静态引用'static对静态'static的引用。