为什么我不能用临时值调用方法?

iDu*_*Jie 1 temporary lifetime rust

我无法调用Foo::new(words).split_first()以下代码

fn main() {
    let words = "Sometimes think, the greatest sorrow than older";
/*
    let foo = Foo::new(words);
    let first = foo.split_first();
*/

    let first = Foo::new(words).split_first();

    println!("{}", first);
}

struct Foo<'a> {
    part: &'a str,
}

impl<'a> Foo<'a> {

    fn split_first(&'a self) -> &'a str {
        self.part.split(',').next().expect("Could not find a ','")
    }

    fn new(s: &'a str) -> Self {
        Foo { part: s }
    }
}
Run Code Online (Sandbox Code Playgroud)

编译器会给我一个错误信息

error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:8:17
   |
8  |     let first = Foo::new(words).split_first();
   |                 ^^^^^^^^^^^^^^^              - temporary value is freed at the end of this statement
   |                 |
   |                 creates a temporary which is freed while still in use
9  | 
10 |     println!("{}", first);
   |                    ----- borrow later used here
   |
   = note: consider using a `let` binding to create a longer lived value
Run Code Online (Sandbox Code Playgroud)

如果我绑定Foo::new(words)first 的值,那么调用该split_first方法没有问题.

这两种调用方法应该直观地相同,但在某种程度上是不同的.

And*_*org 6

\n\n

Foo::new(words).split_first()将被粗略地解释为

\n\n
let tmp = Foo::new(words);\nlet ret = tmp.split_first();\ndrop(tmp);\nret\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果 Rust 允许你这样做,则 中的引用将ret 指向[edit: will be allowed by the type of split_firstto point *] 到现在删除的值中tmp。因此,Rust 不允许这样做,\xe2\x80\x99 是一件好事。如果您用 C++ 编写了等效的单行代码,则\xe2\x80\x99d 会默默地获得未定义的行为。

\n\n

通过let自己编写绑定,您可以将删除延迟到范围末尾,从而扩展了\xe2\x80\x99 可以安全地拥有这些引用的区域。

\n\n

有关更多详细信息,请参阅Rust 参考中的临时生命周期。

\n\n

* 编辑:正如Jmb 所指出的,这个特定示例中的真正问题是类型

\n\n
fn split_first(&\'a self) -> &\'a str\n
Run Code Online (Sandbox Code Playgroud)\n\n

\xe2\x80\x99 不够具体,更好的解决方案是将类型细化为:

\n\n
fn split_first<\'b>(&\'b self) -> &\'a str\n
Run Code Online (Sandbox Code Playgroud)\n\n

可以缩写为:

\n\n
fn split_first(&self) -> &\'a str\n
Run Code Online (Sandbox Code Playgroud)\n\n

这传达了预期的保证,即返回的引用不会指向Foo<\'a>(仅指向字符串本身)。

\n


Jmb*_*Jmb 5

简短的回答:去除'a一生的self参数split_first:fn split_first(&self) -> &'a str(游乐场).

答案很长:

当你编写这段代码时:

struct Foo<'a> {
    part: &'a str,
}

impl<'a> Foo<'a> {
    fn new(s: &'a str) -> Self {
        Foo { part: s }
    }
}
Run Code Online (Sandbox Code Playgroud)

您告诉编译器所有Foo实例都与某个生命周期相关,该生命周期'a必须等于或短于作为参数传递的字符串的生命周期Foo::new.该生命周期'a 可能与每个Foo实例的生命周期不同.当你写下:

let words = "Sometimes think, the greatest sorrow than older";
Foo::new(words)
Run Code Online (Sandbox Code Playgroud)

编译器推断生命周期'a必须等于或短于生命周期words.除非存在任何其他约束,否则编译器将使用生命周期words,'static因此它对程序的整个生命周期都有效.

当您添加以下定义时split_first:

fn split_first(&'a self) -> &'a str
Run Code Online (Sandbox Code Playgroud)

您正在添加一个额外的约束:您说它'a必须等于或短于生命周期self.因此,编译器将占用words临时Foo实例的生命周期和生命周期中较短的一个,这是临时实例的生命周期.@ AndersKaseorg的答案解释了为什么这不起作用.

通过消除'a对寿命self参数,我去相关 'a从临时的寿命,所以编译器可以再次推断'a是的寿命words,这是足够长的程序协同工作.