当借款人范围结束时,不能借入变量

时间:2016-08-02 06:31:42

标签: rust

我无法理解为什么在借款人的范围结束后仍然借用可变借入变量的原因。它看起来与特质使用有关,但我不明白为什么:

fn main() {
    let mut a = 10;
    test::<FooS>(&mut a);
    println!("out {:?}", a)
}

trait Foo<'a> {
    fn new(data: &'a mut u32) -> Self;
    fn apply(&mut self);
}

struct FooS<'a> {
    data: &'a mut u32,
}

impl<'a> Foo<'a> for FooS<'a> {
    fn new(data: &'a mut u32) -> Self {
        FooS { data: data }
    }

    fn apply(&mut self) {
        *self.data += 10;
    }
}

fn test<'a, F>(data: &'a mut u32)
    where F: Foo<'a>
{
    {
        // let mut foo = FooS {data: data}; // This works fine
        let mut foo: F = Foo::new(data);
        foo.apply();
    } // foo scope ends here
    println!("{:?}", data); // error
} // but borrowed till here

try online

error: cannot borrow `data` as immutable because `*data` is also borrowed as mutable [--explain E0502]
   --> <anon>:34:22
31  |>         let mut foo: F = Foo::new(data);
    |>                                   ---- mutable borrow occurs here
...
34  |>     println!("{:?}", data); // error
    |>                      ^^^^ immutable borrow occurs here
35  |> } // but borrowed till here
    |> - mutable borrow ends here

1 个答案:

答案 0 :(得分:8)

test函数要求F类型实现Foo<'a>'a有一个传递给函数的生命周期参数。生命周期参数总是表示比函数调用更长寿命的生命周期 - 因为调用者无法提供寿命更短的引用;你如何从另一个函数传递对局部变量的引用? - 并且为了借用检查(函数的本地),编译器认为借用涵盖整个函数调用。

因此,当您从调用F创建Foo::new的实例时,您创建了一个借用生命周期'a的对象,这个生命周期覆盖整个函数调用。

重要的是要理解当你调用test::<FooS>时,编译器实际填写FooS<'a>的生命周期参数,因此你最终调用test::<FooS<'a>>,其中'a是覆盖包含函数调用的语句的区域(因为&mut a是临时表达式)。因此,编译器认为FooS中构造的test会在语句结束时通过调用test借用一些内容!

让我们将其与非通用版本进行对比:

let mut foo = FooS {data: data};

在此版本中,编译器为FooS<'a>中的test而不是main选择具体的生命周期,因此它将选择从{{{}}末尾延伸的块后缀。 1}}语句到块的末尾,这意味着let的下一次借用不重叠,并且没有冲突。

您真正想要的是data实施F一段Foo<'x>,其生命周期'x短于'a,最重要的是,生命周期必须是函数,而不是'a之类的封闭函数。

Rust目前解决这个问题的方法是排名较高的特质界限。它看起来像这样:

fn test<'a, F>(data: &'a mut u32)
    where F: for<'x> Foo<'x>
{
    {
        let mut foo: F = Foo::new(data);
        foo.apply();
    }
    println!("{:?}", data);
}

简而言之,它意味着 F类型必须为每个可能的Foo<'x> 实施'x

虽然此版本的test自行编译,但我们实际上无法提供满足此约束的类型,因为对于每个可能的生命周期'a,都有一个不同的类型FooS<'a>只实现Foo<'a>。如果FooS没有生命周期参数且Foo的{​​{1}}的impl看起来像这样:

FooS

然后就可以了,因为有一种类型impl<'a> Foo<'a> for FooS { 可以为每个可能的生命周期FooS实施Foo<'a>

当然,你不能删除'a上的生命周期参数,因为它包含一个借来的指针。这个问题的正确解决方案是Rust还没有的功能:能够将类型构造函数(而不是完全构造的类型)作为泛型参数传递给函数。使用此功能,我们可以使用FooS调用test,这是一个需要生命周期参数来生成具体类型的类型构造函数,而无需在调用站点指定具体生命周期,调用者将能够提供自己的一生。