语法特性叫 reborrowing, the book 和 the reference 都没写清楚, 实际人人都用,根本离不开的。
// 已有 I 和 X1 以及 `impl From<&mut I> for X1`
struct I(i32);
struct X1;
impl From<&mut I> for X1 {
fn from(p: &mut I) -> X1 {
p.0 = 1;
X1
}
}
// 必须引入这个中间函数
fn x1(p: &mut I) -> X1 {
X1::from(p)
}
// 我想要 `impl From<&mut I> for XT`
struct XT(X1, X1);
impl From<&mut I> for XT {
fn from(p: &mut I) -> XT {
XT(x1(p), x1(p))
}
}
// value used here after move
fn from_twice_fail(p: &mut I) {
let x11 = X1::from(p);
let x12 = X1::from(p);
}
// 跟上面的函数有啥区别,为什么可以?
fn from_twice(p: &mut I) {
let x11 = x1(p);
let x12 = x1(p);
}
1
共 12 条评论, 1 页
评论区
写评论XT(x1(p), x1(p)): p在第一个 x1(p)已经被move了。
结合我自己的搜寻和对下面代码的观察,我有这么几个想法:
struct A; struct B; trait Hey<T> { fn hey(p: T, q: T); } impl Hey<u32> for B { fn hey(p: u32, q: u32) { todo!() } } impl Hey<&mut A> for B { fn hey(p: &mut A, q: &mut A) { todo!() } } fn hey_hey(p: &mut A, q: &mut A) { // <B as Hey<_>> 中的 _ 类型有两种可能,需要被推断: <B as Hey<_>>::hey(p, q); // 编译器抱怨 p 已被移走,但是 q 却依然可以使用: <B as Hey<_>>::hey(p, q); }
--
👇
NoReligion: 这个重借用问题似乎只和
From<_>
有关……use std::ops::Add; struct I(i32); struct S; trait Tr<T> { fn g(p: T) -> Self; } impl Tr<&mut I> for S { fn g(p: &mut I) -> S { p.0 = 1; S } } impl Add<&mut I> for S { type Output = S; fn add(self, p: &mut I) -> S { p.0 = 3; self } } impl From<&mut I> for S { fn from(p: &mut I) -> S { p.0 = 2; S } } fn _test(p: &mut I) { let _x11 = S::g(p); let _x12 = S::g(p); // 可自动重借用 } fn _test2(p: &mut I) { let _x11 = S::add(S, p); let _x12 = S::add(S, p); // 可自动重借用 } fn _fail(p: &mut I) { let _x11 = S::from(p); let _x12 = S::from(p); // 不可自动重借用,"use of moved value: `p`" }
--
👇
viruscamp: 稍稍测试了下,应该是泛型函数或trait定义就有
&mut
的,可以自动重借用,而泛型参数被替换成&mut X
的不能自动重借用struct X; impl From<&mut i32> for X { fn from(_: &mut i32) -> X { X } } fn from<F, T: From<F>>(f: F) -> T { T::from(f) } let mut i = 4; let x = &mut i; let _: X = from(x); // 此处不会自动重借用 let _: X = from(x); // 第二次调用失败 fn from2<'a, F, T: From<&'a mut F>>(f: &'a mut F) -> T { T::from(f) } let _: X = from2(x); // 可以自动重借用 let _: X = from2(x);
这个重借用问题似乎只和
From<_>
有关……use std::ops::Add; struct I(i32); struct S; trait Tr<T> { fn g(p: T) -> Self; } impl Tr<&mut I> for S { fn g(p: &mut I) -> S { p.0 = 1; S } } impl Add<&mut I> for S { type Output = S; fn add(self, p: &mut I) -> S { p.0 = 3; self } } impl From<&mut I> for S { fn from(p: &mut I) -> S { p.0 = 2; S } } fn _test(p: &mut I) { let _x11 = S::g(p); let _x12 = S::g(p); // 可自动重借用 } fn _test2(p: &mut I) { let _x11 = S::add(S, p); let _x12 = S::add(S, p); // 可自动重借用 } fn _fail(p: &mut I) { let _x11 = S::from(p); let _x12 = S::from(p); // 不可自动重借用,"use of moved value: `p`" }
--
👇
viruscamp: 稍稍测试了下,应该是泛型函数或trait定义就有
&mut
的,可以自动重借用,而泛型参数被替换成&mut X
的不能自动重借用struct X; impl From<&mut i32> for X { fn from(_: &mut i32) -> X { X } } fn from<F, T: From<F>>(f: F) -> T { T::from(f) } let mut i = 4; let x = &mut i; let _: X = from(x); // 此处不会自动重借用 let _: X = from(x); // 第二次调用失败 fn from2<'a, F, T: From<&'a mut F>>(f: &'a mut F) -> T { T::from(f) } let _: X = from2(x); // 可以自动重借用 let _: X = from2(x);
稍稍测试了下,应该是泛型函数或trait定义就有
&mut
的,可以自动重借用,而泛型参数被替换成&mut X
的不能自动重借用struct X; impl From<&mut i32> for X { fn from(_: &mut i32) -> X { X } } fn from<F, T: From<F>>(f: F) -> T { T::from(f) } let mut i = 4; let x = &mut i; let _: X = from(x); // 此处不会自动重借用 let _: X = from(x); // 第二次调用失败 fn from2<'a, F, T: From<&'a mut F>>(f: &'a mut F) -> T { T::from(f) } let _: X = from2(x); // 可以自动重借用 let _: X = from2(x);
我是看 miri 的 stacked borrow 才搞懂 reborrow 的
正如issue里所说,reborrow并没有文档记录,哪些情况下会有reborrow全部在引擎盖之下,幸运的是借助rust-analyzer可以一窥究竟:
"rust-analyzer.inlayHints.expressionAdjustmentHints.enable": "always", // 开启reborrow提示 "rust-analyzer.inlayHints.lifetimeElisionHints.enable": "always", // 开启生命周期提示
通过实验会发现直接调用 trait 方法时,其中的 Self 类型并不会发生reborrow,所以两次调用 X1::from(p) 会失败。
嗯,基本思路是对的,只不过注意:
对于你的代码,显式的重借可以解决
fn from_twice_fail(p: &mut I) { let x11 = X1::from(&mut *p); let x12 = X1::from(&mut *p); }
这涉及 reborrow 和泛型的交互,见 https://github.com/rust-lang/rust/issues/85161,而且据此并不被认为是一个错误。
https://alabaster-linen-4dc.notion.site/Rust-86f927bca1794b3b95e3b5ab5f81b9c4
有名字就好查了 better documentation of reborrowing
差不多让我理解的是这段:
我的理解是:
fn x2(p: &mut I) { { let p = &mut *p; // 隐式自动生成的 reborrow, 最终不会违反借用规则 let x11 = x1(p); } { let p = &mut *p; let x12 = x1(p); } } fn x2_from(p: &mut I) { // 应该 reborrow 的,但没有实现 { let p = &mut *p; let x11 = X1::from(p); } { let p = &mut *p; let x12 = X1::from(p); } }
--
👇
苦瓜小仔: reborrow 规则而已。
reborrow 规则而已。
// 在发现多包一层函数可以绕过之前,写过这种东西 trait MyFrom<T> { fn new(&mut T) -> (Self, &mut T); } // 可以用,完全符合借用规则,就是感觉非常恶心,有种吃了吐,吐了吃,人*蜈蚣的感觉。
// 简单思考下,这里报错是符合一般借用规则的 fn from_twice_fail(p: &mut I) { let x11 = X1::from(p); let x12 = X1::from(p); } // 这里不报错,应该是某条例外规则,哪里有这个规则的说明? fn from_twice(p: &mut I) { let x11 = x1(p); let x12 = x1(p); } // 要是没有例外规则,这个都跑不起来 fn set_pos(p: &mut Pos) { p.set_x(3); p.set_y(4); } struct Pos(i32, i32); impl Pos { fn set_x(&mut self, x: i32) { self.0 = x; } fn set_y(&mut self, y: i32) { self.1 = y; } }