我无法在以下代码中致电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
方法就没有问题。
这两种调用方法在直观上应该相同,但又有所不同。
答案 0 :(得分:5)
简短的回答::删除'a
的{{1}}参数self
(playground)的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>
(仅指向字符串本身)。