Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.4k views
in Technique[技术] by (71.8m points)

rust - Why do I get "cannot move out of `item` because it is borrowed" for a custom type but not a Box?

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:

  1. Why does it complain about the borrow in the if branch while we in else branch? They are supposed to be mutually exclusive, and a borrow in one should not influence another.

  2. If I replace Vec in let mut items = Vec::<P<Item>>::new(); with Vec<Box<Item>> and uncomment line AAA and comment line BBB, then it compiles. Both Box and P implement Deref, so the item.node expression should be the same.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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 and P implement Deref, so the item.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:


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...