问题
Code:
use std::collections::HashSet;
use std::{mem, ptr, fmt};
use std::ops::Deref;
enum Unsafety {
Normal
}
enum ImplPolarity { Positive }
struct TraitRef;
struct Ty;
struct ImplItem;
enum ItemKind {
Impl(Unsafety,
ImplPolarity,
Option<TraitRef>, // (optional) trait this impl implements
Box<Ty>, // self
),
}
struct Item {
node: ItemKind,
}
pub struct P<T: ?Sized> {
ptr: Box<T>
}
impl<T: 'static> P<T> {
pub fn unwrap(self) -> T {
*self.ptr
}
}
impl<T: ?Sized> Deref for P<T> {
type Target = T;
fn deref(&self) -> &T {
&self.ptr
}
}
fn main() {
let mut items = Vec::<P<Item>>::new();
let mut item2: Item;
for item in items.drain(..) {
if let ItemKind::Impl(Unsafety::Normal,
ImplPolarity::Positive,
Some(ref trait_type),
ref for_type) = item.node {
} else {
// item2 = *item; // AAA
item2 = item.unwrap(); // BBB
}
}
}
Produce the compile-time error:
error[E0505]: cannot move out of `item` because it is borrowed
--> /home/xxx/.emacs.d/rust-playground/at-2017-07-29-204629/snippet.rs:64:21
|
61 | ref for_type) = item.node {
| ---- borrow of `item` occurs here
...
64 | item2 = item.unwrap();
I do not understand two things:
Why does it complain about the borrow in the
if
branch while we inelse
branch? They are supposed to be mutually exclusive, and a borrow in one should not influence another.If I replace
Vec
inlet mut items = Vec::<P<Item>>::new();
withVec<Box<Item>>
and uncomment lineAAA
and comment lineBBB
, then it compiles. BothBox
andP
implementDeref
, so theitem.node
expression should be the same.
回答1:
Here's a much clearer example:
struct Item;
struct P<T> {
ptr: Box<T>,
}
impl<T> P<T> {
fn new(v: T) -> Self {
P { ptr: Box::new(v) }
}
}
impl<T> std::ops::Deref for P<T> {
type Target = T;
fn deref(&self) -> &T {
&self.ptr
}
}
fn main() {
let mut item = P::new(Item);
// let mut item = Box::new(Item);
*item;
}
Both
Box
andP
implementDeref
, so theitem.node
expression should be the same.
Hard truth time: moving out of Box
is special-cased in the compiler. It does not use Deref
. Moving out of a Box
deallocates the memory and gives you ownership. It is not possible to implement this special ability ourselves.
Maybe at some point in the future a hypothetical trait like DerefMove
will be added. This trait is hard to get right. There have been a number of attempts for an RFC for it, but none are currently open.
See also:
- Dereferencing Box<T> gives back value instead of reference
- How can I reuse a box that I have moved the value out of?
来源:https://stackoverflow.com/questions/45392688/why-do-i-get-cannot-move-out-of-item-because-it-is-borrowed-for-a-custom-typ