我无法理解为什么在借款人的范围结束后仍然借用可变借入变量.看起来它与特质使用有关,但我不明白为什么:
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
该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};
Run Code Online (Sandbox Code Playgroud)
在这个版本中,编译器为FooS<'a>in test而不是in 选择具体的生命周期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);
}
Run Code Online (Sandbox Code Playgroud)
用语言来表示类型F必须Foo<'x>为每一种可能的方式实现'x.
虽然这个版本的test编译本身,我们实际上无法提供满足此约束的类型,因为对于每个可能的生命周期'a,都有一个FooS<'a>仅实现的独特类型Foo<'a>.如果FooS没有生命周期参数,并且Foofor 的impl FooS看起来像这样:
impl<'a> Foo<'a> for FooS {
Run Code Online (Sandbox Code Playgroud)
那就没关系了,因为有一种类型FooS可以实现Foo<'a>每一个可能的生命周期'a.
当然,你不能删除生命周期参数FooS,因为它包含一个借来的指针.这个问题的正确解决方案是Rust还没有的功能:能够将类型构造函数(而不是完全构造的类型)作为泛型参数传递给函数.有了这个功能,我们可以调用test与FooS,需要一辈子的参数来产生一个具体类型,没有在调用点指定的混凝土寿命型构造,调用者将能够提供自己的寿命.