2016-08-02 33 views
9

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

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

https://play.rust-lang.org/?gist=dabe17d66a14e72c2ca67e064ca26601&version=nightly&backtrace=0的作品。所以...... – Jacob

+0

不过,这似乎是一个错误。 – JDemler

回答

8

test功能需要型F实现Foo<'a>'a有一个传递给函数的生命周期参数。生命周期参数总是表示生命期长于功能调用时间 - 因为调用者无法提供寿命较短的参考;你怎么能通过另一个功能一个局部变量的参考? - ,并且为了借用检查(对于函数而言是本地的),编译器认为借用包含整个函数调用。

因此,当您从呼叫Foo::new创建F一个实例,您创建一个对象,借用终身'a东西,覆盖整个函数调用一生。

明白,当你调用test::<FooS>,编译器实际上在FooS<'a>一辈子参数填充是非常重要的,所以你最终调用test::<FooS<'a>>,其中'a是覆盖包含函数调用语句区域(因为&mut a是一个临时表达)。因此,编译器认为将在test中构建的FooS将借用test的呼叫直到声明结束!

让我们对比这与非泛型版本:

let mut foo = FooS {data: data}; 

在这个版本中,编译器选择的FooS<'a>混凝土的寿命在test,而不是在main,所以它会选择从末端延伸块后缀的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没有寿命参数和FooFooS的IMPL是这样的:

impl<'a> Foo<'a> for FooS { 

那么这将是很好的,因为有一个单一类型FooS,对每一个可能一辈子'a实现Foo<'a>

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

+0

感谢您的详细解答。对我来说仍然是一个不明确的时刻。正如你所说的那样_“生命期参数总是表示寿命比函数调用寿命更长,并且为了借用检查(对函数来说是局部的),编译器认为借用覆盖了整个函数调用。”_。为什么编译器编译器考虑这个?我们有一生能活得久一些,然后是功能范围。但是与借用有关的内容如何涵盖整个函数调用?是实施的细节还是规范的一部分? –

+0

Rust没有适当的“规范”,所以实现与您将获得的规范最接近。对于你的其他问题,请参阅我的编辑。 –