为什么Rust的临时值有时是引用的,有时不是?

lqfhib0f  于 2023-06-06  发布在  其他
关注(0)|答案(1)|浏览(142)

首先,下面的代码是正确的:

fn main() {
    let a = &get_i32();
    println!("{}", a);
}
fn get_i32() -> i32 {
    return 100;
}

但是下面的代码出错:

fn main() {
    let a;
    a = &get_i32();
    println!("{}", a);
}
error[E0716]: temporary value dropped while borrowed
 --> src/bin/rust_course.rs:8:10
  |
8 |     a = &get_i32();
  |          ^^^^^^^^^- temporary value is freed at the end of this statement
  |          |
  |          creates a temporary value which is freed while still in use
9 |     println!("{}", a);
  |                    - borrow later used here
  |
help: consider using a `let` binding to create a longer lived value
  |
8 ~     let binding = get_i32();
9 ~     a = &binding;
  |

For more information about this error, try `rustc --explain E0716`.

这两段代码的本质区别是什么?我知道&get_i32()总是返回一个临时值,所以它应该总是报告一个错误。
类似的问题:

fn main() {
    let s1 = &String::from("hello world");
    println!("{}", s1);
    let s2 = String::from("hello world").as_str();
    println!("{}", s2);
}
error[E0716]: temporary value dropped while borrowed
 --> src/bin/rust_course.rs:6:14
  |
6 |     let s2 = String::from("hello world").as_str();
  |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^         - temporary value is freed at the end of this statement
  |              |
  |              creates a temporary value which is freed while still in use
7 |     println!("{}", s2);
  |                    -- borrow later used here
  |
help: consider using a `let` binding to create a longer lived value
  |
6 ~     let binding = String::from("hello world");
7 ~     let s2 = binding.as_str();
  |

For more information about this error, try `rustc --explain E0716`.

s1和s2有什么区别?
还有一个更类似的问题:

fn main() {
    print(String::from("hello world").as_str());
}

fn print(str: &str) {
    println!("{}", str);
}

上面的代码是正确的,但我不明白为什么String::from("hello world").as_str()可以传递给函数,但不能赋值给变量。

fnvucqvd

fnvucqvd1#

s1和s2有什么区别?
答案是暂时延长寿命。可悲的是,这是一个有点非正式的过程(正如页面所指出的那样,它可能会发生变化),但从广义上讲,用于绑定文字引用的let(因此&something)可以触发生命周期延长,其中something将获得隐式临时。所以let a = &get_i32();let s1 = &String::from("hello world");从中受益。
a = &get_i32();不支持,因为TLE只支持let
let s2 = String::from("hello world").as_str();也不会,因为临时变量的生命周期被延长到 * 语句 * 的末尾,所以链本质上编译成块中的调用序列,例如:

let s2 = {
    let _temp = String::from("hello world");
    _temp.as_str()
    // _temp is dropped here so `as_str` becomes invalid
};

但是请注意,临时语句将一直持续到 * 语句 * 的末尾,以防

print(String::from("hello world").as_str());

该语句持续到print的末尾,本质上是:

{
    let _temp1 = String::from("hello world");
    let _temp2 = _temp1.as_str();
    print(_temp2)
};

这完全没问题
这也是为什么你可以这样写:

match &Some(String::new()).as_deref() {
        Some("") => println!("ok"),
        Some(_) => println!("??"),
        None => println!("ko"),
    }

整个match是一个单一的语句,所以Option<String>临时的生命直到它的结束,这意味着我们可以获得对内部和外部值的引用,并使用&Option<&str>来处理我们从未绑定过的值(这是无意义的代码,但它显示了原理,这是我想到的第一件事)。
然而,也有一些情况下,它会导致问题,例如:你尝试match在一个借用然后移动原始值在一个分支。这种情况在非词法生存期(和借用检查)中已经不那么常见了,但它仍然会不时发生。

相关问题