为什么不能调用具有临时值的方法?

时间:2019-01-17 06:04:18

标签: rust lifetime temporary

我无法在以下代码中致电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 }
    }
}

编译器会给我一条错误消息

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

如果我先绑定Foo::new(words)的值,然后调用split_first方法就没有问题。

这两种调用方法在直观上应该相同,但又有所不同。

2 个答案:

答案 0 :(得分:5)

简短的回答::删除'a的{​​{1}}参数selfplayground)的split_first生存期。< / p>

长答案:

编写此代码时:

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 } } } 实例都与某个生存期Foo相关,生存期必须等于或短于作为参数传递给'a的字符串的生存期。该生存期Foo::new 可能与每个'a实例的生存期不同。当您随后写:

Foo

编译器推断生存期let words = "Sometimes think, the greatest sorrow than older"; Foo::new(words) 必须等于或短于'a的生存期。除非有其他限制,否则编译器将使用words的生存期,即words,因此对于程序的整个生存期都是有效的。

添加'static的定义时:

split_first

您要添加一个额外的约束:您说fn split_first(&'a self) -> &'a str 也必须等于或短于'a的生存期。因此,编译器将使用self的生存期和临时words实例的生存期(即临时生存期)中的较短者。 @AndersKaseorg's answer解释了为什么不起作用。

通过删除Foo参数上的'a生命周期,我将self与临时生命周期解相关,因此编译器可以再次推断'a'a的生存期,足以使程序正常工作。

答案 1 :(得分:2)

Foo::new(words).split_first()大致解释为

let tmp = Foo::new(words);
let ret = tmp.split_first();
drop(tmp);
ret

如果Rust允许您执行此操作,则ret中的引用将指向 [编辑:类型为split_first的指向将指向 *]放入tmp现在丢弃的值中。因此Rust不允许这样做是一件好事。如果您使用C ++编写了等效的单行代码,那么您将无声地得到未定义的行为。

通过自己编写let绑定,您可以将放置延迟到合并范围的末尾,从而扩展了可以安全使用这些引用的区域。

有关更多详细信息,请参见Rust参考中的temporary lifetimes

*编辑:与pointed out by Jmb一样,此特定示例中的真正问题是类型

fn split_first(&'a self) -> &'a str

不够具体,一种更好的解决方案是将类型细化为:

fn split_first<'b>(&'b self) -> &'a str

可以缩写:

fn split_first(&self) -> &'a str

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