为什么self.f2()
以下代码中的调用会使借用检查程序运行?是不是在另一个范围内的else块?这是一个相当难的问题!
use std::str::Chars;
struct A;
impl A {
fn f2(&mut self) {}
fn f1(&mut self) -> Option<Chars> {
None
}
fn f3(&mut self) {
if let Some(x) = self.f1() {
} else {
self.f2()
}
}
}
fn main() {
let mut a = A;
}
Run Code Online (Sandbox Code Playgroud)
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/main.rs:16:13
|
13 | if let Some(x) = self.f1() {
| ---- first mutable borrow occurs here
...
16 | self.f2()
| ^^^^ second mutable borrow occurs here
17 | }
| - first borrow ends here
Run Code Online (Sandbox Code Playgroud)
自我借款的范围不是以self.f1()
电话开始和结束的吗?一旦来自f1()
已返回的呼叫f1()
不再使用自己,因此借用检查器不应该对第二次借用有任何问题.请注意以下代码也失败了......
// ...
if let Some(x) = self.f1() {
self.f2()
}
// ...
Run Code Online (Sandbox Code Playgroud)
我认为次贷应该是在这里很好,因为f1
并f3
没有使用self
在同一时间f2
.
我举了一个例子来展示这里的范围规则:
struct Foo {
a: i32,
}
impl Drop for Foo {
fn drop(&mut self) {
println!("Foo: {}", self.a);
}
}
fn generate_temporary(a: i32) -> Option<Foo> {
if a != 0 { Some(Foo { a: a }) } else { None }
}
fn main() {
{
println!("-- 0");
if let Some(foo) = generate_temporary(0) {
println!("Some Foo {}", foo.a);
} else {
println!("None");
}
println!("-- 1");
}
{
println!("-- 0");
if let Some(foo) = generate_temporary(1) {
println!("Some Foo {}", foo.a);
} else {
println!("None");
}
println!("-- 1");
}
{
println!("-- 0");
if let Some(Foo { a: 1 }) = generate_temporary(1) {
println!("Some Foo {}", 1);
} else {
println!("None");
}
println!("-- 1");
}
{
println!("-- 0");
if let Some(Foo { a: 2 }) = generate_temporary(1) {
println!("Some Foo {}", 1);
} else {
println!("None");
}
println!("-- 1");
}
}
Run Code Online (Sandbox Code Playgroud)
这打印:
-- 0
None
-- 1
-- 0
Some Foo 1
Foo: 1
-- 1
-- 0
Some Foo 1
Foo: 1
-- 1
-- 0
None
Foo: 1
-- 1
Run Code Online (Sandbox Code Playgroud)
简而言之,似乎if
子句中的表达式同时存在于if
块和else
块中.
一方面它并不奇怪,因为确实需要比if
块更长寿,但另一方面它确实阻止了有用的模式.
如果您更喜欢视觉说明:
if let pattern = foo() {
if-block
} else {
else-block
}
Run Code Online (Sandbox Code Playgroud)
desugars into:
{
let x = foo();
match x {
pattern => { if-block }
_ => { else-block }
}
}
Run Code Online (Sandbox Code Playgroud)
虽然你更喜欢它去了:
bool bypass = true;
{
let x = foo();
match x {
pattern => { if-block }
_ => { bypass = false; }
}
}
if not bypass {
else-block
}
Run Code Online (Sandbox Code Playgroud)
你并不是第一个因此被绊倒的人,所以尽管改变了某些代码的含义(特别是警卫),这可能会在某个时候解决.
小智 6
这很烦人,但您可以通过引入内部作用域并稍微更改控制流来解决此问题:
fn f3(&mut self) {
{
if let Some(x) = self.f1() {
// ...
return;
}
}
self.f2()
}
Run Code Online (Sandbox Code Playgroud)
正如评论中所指出的,这不需要额外的大括号。这是因为if
orif...let
表达式具有隐式作用域,并且借用持续用于此作用域:
fn f3(&mut self) {
if let Some(x) = self.f1() {
// ...
return;
}
self.f2()
}
Run Code Online (Sandbox Code Playgroud)
这是 Sandeep Datta 和 mbrubeck 之间的 IRC 聊天记录:
mbrubeck: std:tr::Chars 包含对创建它的字符串的借用引用。完整的类型名称是
Chars<'a>
. 所以f1(&mut self) -> Option<Chars>
没有省略 isf1(&'a mut self) -> Option<Chars<'a>>
这意味着self
只要返回值f1
在范围内就保持借用。Sandeep Datta:我可以将 'b 用于 self 并将 'a 用于 Chars 来避免这个问题吗?
mbrubeck:如果您实际上是从
self
. 但是,如果您可以从&self -> Chars
(而不是&mut self -> Chars
)创建一个函数来解决问题。
归档时间: |
|
查看次数: |
2384 次 |
最近记录: |