在Rust编程语言的第19.2章中,以下示例编译时没有任何错误。我从issue #1834发现,有一个新的终身省略规则隐含地使's
比'c
更长。
虽然我找不到这个新的省略规则的详细解释,但我想它不仅仅是更长,更明确的约束的隐式版本:<'c, 's: 'c>
。我想我的困惑可能不是关于这个新的省略规则,但当然我可能错了。
我的理解是,parse_context
取得了context
的所有权,因为它没有被借用但实际上转移到了该功能。仅此一点意味着context
的生命周期应与其拥有的函数的生命周期相匹配,无论我们在Context
和Parser
中定义的生命周期和约束如何。
基于这些定义,context
超过临时Parser
的部分对我来说是完全合理的(毕竟,我们定义了更长的生命周期),但是当&str
在context
结束时超出范围时,parse_context
引用的部分没有被丢弃我仍然可以放心地回来 - 让我感到困惑。
我错过了什么?编译器如何推断返回的&str
的生命周期?
更新的例子
struct Context<'s>(&'s str);
struct Parser<'c, 's>
{
context: &'c Context<'s>,
}
impl<'c, 's> Parser<'c, 's>
{
fn parse(&self) -> Result<(), &'s str>
{
Err(self.context.0)
}
}
fn parse_context(context: Context) -> Result<(), &str>
{
Parser { context: &context }.parse()
}
fn main()
{
let mut s = String::new();
s += "Avail";
s += "able?";
if let Err(text) = parse_context(Context(&s))
{
println!("{}", text);
}
}
struct Context<'s>(&'s str);
→Context
类型的值包含一些带有生命周期's
的字符串。这个生命周期至少与上下文的生命周期一样长,但可能更长。
struct Parser<'c, 's>
{
context: &'c Context<'s>,
}
→Parser
类型的值包含对具有生命周期'c
的上下文的引用。此上下文包含一个字符串,其中包含一些其他生命周期's
。
impl<'c, 's> Parser<'c, 's>
{
fn parse(&self) -> Result<(), &'s str>
{
Err(self.context.0)
}
}
→函数parse
返回一个带有生命周期's
的值,即。与生成在上下文中的字符串具有相同的生命周期,这与上下文本身的生命周期不同。
fn parse_context(context: Context) -> Result<(), &str>
{
Parser { context: &context }.parse()
}
→我不确定指定的确切位置,但显然编译器推断返回字符串的生命周期与用于上下文的's
参数相同。请注意,即使将上下文本身移动到parse_context
中,这只会影响上下文本身,而不会影响它包含的字符串。
fn main()
{
let mut s = String::new();
→创建一个有效的新字符串,直到main
结束
s += "Avail";
s += "able?";
if let Err(text) = parse_context(Context(&s))
→创建一个新的上下文并将其移动到parse_context
。它将在parse_context
结束时自动删除。它包含对s
字符串的引用,该字符串在main
结束时有效,parse_context
返回与s
相同的生命周期的字符串⇒text
有效直到main
结束。
{
println!("{}", text);
}
}
→没问题:text
有效直到main
结束。
您没有返回对函数的拥有值的引用。您将返回传入的引用的副本。
您的函数是身份函数的精细版本:
fn parse_context(s: &str) -> &str {
s
}
在您的实际代码中,您将引用包含字符串切片的结构,然后引用另一个对字符串切片的引用,但所有这些引用都将被丢弃。
例如,parse
中有一个不需要的引用:
fn parse(&self) -> Result<(), &'s str> {
Err( self.context.0)
// ^ no & needed
}
此外,如果您启用更多lints,您将被迫为函数签名添加更多生命周期,这可能会使事情变得更加清晰:
#![deny(rust_2018_idioms)]
fn parse_context(context: Context<'_>) -> Result<(), &'_ str> {
Parser { context: &context }.parse()
}
也可以看看:
虽然我找不到这个新的省略规则的详细解释,
T: 'a
inference in structs在版本指南中。
感谢Jmb的评论以及Shepmaster的一些回答,我现在确实清楚我的困惑是关于RAII规则和所有权。
也就是说,字符串是在main
范围内创建的,匿名的Context
实例没有获取它只是借用引用的字符串的所有权,因此即使实例在parse_context
的末尾被删除以及借用的引用,引用也是如此复制到Err
对象仍然存在并指向现有字符串 - 因此我们使用约束生命周期变量,编译器能够推断内部字符串引用的生命周期。