为什么我可以返回对函数的拥有值的引用?

问题描述 投票:1回答:3

在Rust编程语言的第19.2章中,以下示例编译时没有任何错误。我从issue #1834发现,有一个新的终身省略规则隐含地使's'c更长。

虽然我找不到这个新的省略规则的详细解释,但我想它不仅仅是更长,更明确的约束的隐式版本:<'c, 's: 'c>。我想我的困惑可能不是关于这个新的省略规则,但当然我可能错了。

我的理解是,parse_context取得了context的所有权,因为它没有被借用但实际上转移到了该功能。仅此一点意味着context的生命周期应与其拥有的函数的生命周期相匹配,无论我们在ContextParser中定义的生命周期和约束如何。

基于这些定义,context超过临时Parser的部分对我来说是完全合理的(毕竟,我们定义了更长的生命周期),但是当&strcontext结束时超出范围时,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);
    }
}
rust lifetime ownership
3个回答
1
投票
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结束。


2
投票

您没有返回对函数的拥有值的引用。您将返回传入的引用的副本。

您的函数是身份函数的精细版本:

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在版本指南中。


-1
投票

感谢Jmb的评论以及Shepmaster的一些回答,我现在确实清楚我的困惑是关于RAII规则和所有权。

也就是说,字符串是在main范围内创建的,匿名的Context实例没有获取它只是借用引用的字符串的所有权,因此即使实例在parse_context的末尾被删除以及借用的引用,引用也是如此复制到Err对象仍然存在并指向现有字符串 - 因此我们使用约束生命周期变量,编译器能够推断内部字符串引用的生命周期。

© www.soinside.com 2019 - 2024. All rights reserved.