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

时间:2019-04-23 23:00:21

标签: rust lifetime ownership

Rust编程语言的第19.2章中,以下示例可正确编译。我从issue #1834中发现,存在一条新的生存期删除规则,该规则隐含地使's长于'c

尽管我找不到关于此新省略规则的详细说明,但我想它不只是 just 更长,更明确的约束的隐式版本:<'c, 's: 'c> 。我认为,但是我的困惑可能与新的省略规则无关,但是我对此可能是错误的。

我的理解是,parse_context拥有context的所有权,因为它尚未借用,但实际上已移至该函数。这对我而言意味着context的生存期应该与它所拥有的函数的生存期相匹配,而不考虑我们在ContextParser中定义的生存期和约束。

基于这些定义,context超过临时Parser的那部分对我来说是很有意义的(毕竟,我们定义了更长的生存期),但是&str的那部分当contextparse_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);
    }
}

3 个答案:

答案 0 :(得分:2)

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

您的函数是身份函数的详尽版本:

fn parse_context(s: &str) -> &str {
    s
}

在您的真实代码中,您引用了包含字符串切片的结构,然后又引用了该字符串切片,但是所有这些引用都被丢弃了。

例如,parse中有不需要的引用:

fn parse(&self) -> Result<(), &'s str> {
    Err( self.context.0)
    //  ^ no & needed
}

另外,如果启用更多的棉绒,则将被迫为函数签名添加更多生命周期,这可能会使事情更加清楚:

#![deny(rust_2018_idioms)]

fn parse_context(context: Context<'_>) -> Result<(), &'_ str> {
    Parser { context: &context }.parse()
}

另请参阅:

  

尽管我找不到有关此新省略规则的详细说明,

T: 'a inference in structs在版本指南中。

答案 1 :(得分:1)

struct Context<'s>(&'s str);

→类型Context的值包含一个寿命为's的字符串。该生存期隐含地至少与上下文的生存期一样长,但可能更长。

struct Parser<'c, 's>
{
    context: &'c Context<'s>,
}

→类型为Parser的值持有对生存期为'c的上下文的引用。该上下文包含具有 other 生存期'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返回与stext有效的生存期相同的字符串直到main的结尾。

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

→没问题:text有效期至main结束。

答案 2 :(得分:-1)

感谢Jmb的评论和Shepmaster的一些回答,对于我来说,现在的确是我对RAII规则和所有权的困惑。

也就是说,该字符串是在main范围内创建的,匿名Context实例是,它只是借用了引用,因此,即使借用了引用,因此即使当实例与借用的引用一起放在parse_context的末尾时,复制到Err对象的引用仍然存在,并且指向现有的字符串-因此,我们使用了受约束的生存期变量和编译器能够推断内部字符串引用的生存期。