我发现了一个例子,手动内联一个函数会改变借用检查器对该函数的处理方式,导致它无法编译通过。这可能是依赖于函数签名中的信息。如何在内联版本中提供这些信息?
我的想法是怎么工作的
让'a
和'b
成为生命周期,'a
短于'b
(可以写成'b: 'a
)。
假设我有一个p:&'b mut f32
。我可以暂时借用p
(使用&mut p
)来获得q:&'a mut &'b mut f32
。
- 我是否正确理解了
&'a mut &'b mut f32
等同于&'a mut &'a mut f32
因为'b: 'a
?
然后我可以解引用q
(使用*q
)来获得r:&'a mut f32
。我可以通过r
向f32
写入数据(使用*r = something
),并稍后(超出生命周期'a
)通过p
读取该值(使用*p
)。
使用函数调用
这里有一些可行的代码,我认为它使用了上述过程:
fn reborrow<'a, 'b: 'a>(q: &'a mut &'b mut f32) -> &'a mut f32 {
*q
}
fn main() {
let mut x: f32 = 3.142;
let mut p = &mut x;
{
let q = &mut p;
let r = reborrow(q);
*r = 2.718;
}
assert_eq!(*p, 2.718);
}
在reborrow()
函数体中,将*q
替换为q
也能正常工作,因为Rust会在必要时插入缺失的解引用操作。
手动内联
如果我手动内联reborrow()
调用,它将无法编译:
fn main() {
let mut x: f32 = 3.142;
let mut p = &mut x;
{
let q = &mut p;
let r = *q; <-- ERROR REPORTED HERE.
*r = 2.718;
}
assert_eq!(*p, 2.718);
}
error[E0507]: cannot move out of borrowed content
谁拿走了我的玩具?类型推断在思考/缺失什么?
我能以某种方式注释
let
绑定以使编译器推断与先前版本相同的类型吗?
其他尝试
这是另一个可行的版本,但它没有定义名称r
:
fn main() {
let mut x: f32 = 3.142;
let mut p = &mut x;
{
let q = &mut p;
**q = 2.718;
}
assert_eq!(*p, 2.718);
}
这里有一个解决办法,定义了名字r
并且可以工作,但是没有使用相同的借用和解引用顺序:
fn main() {
let mut x: f32 = 3.142;
let mut p = &mut x;
{
let q = &mut p;
let r = &mut **q;
*r = 2.718;
}
assert_eq!(*p, 2.718);
}
我制作了一个游乐场,将所有四个版本组合在一起。
{}
技巧在这里不起作用,这似乎表明它在某种程度上是不同的。 - Shepmaster