2017-10-19 58 views
14

爲什麼n1_mut在這個例子中仍然有效?它已被轉移到Option::Some所以它不應該是無效的?爲什麼在指針移動後爲指針的成員賦值仍然有效?

struct MyRecordRec2<'a> { 
    pub id: u32, 
    pub name: &'a str, 
    pub next: Box<Option<MyRecordRec2<'a>>> 
} 

#[test] 
fn creating_circular_recursive_data_structure() { 
    let mut n1_mut = MyRecordRec2 { 
     id: 1, 
     name: "n1", 
     next: Box::new(None) 
    }; 

    let n2 = MyRecordRec2 { 
     id: 2, 
     name: "n2", 
     next: Box::new(Some(n1_mut)) 
    }; 

    //Why is n1_mut still valid? 
    n1_mut.next = Box::new(Some(n2)); 
} 

以下不熟悉的「使用移動值的」編譯錯誤:

#[test] 
fn creating_and_freezing_circular_recursive_data_structure() { 
    let loop_entry = { 
     let mut n1_mut = MyRecordRec2 { 
      id: 1, 
      name: "n1", 
      next: Box::new(None), 
     }; 

     let n2 = MyRecordRec2 { 
      id: 2, 
      name: "n2", 
      next: Box::new(Some(n1_mut)), 
     }; 

     n1_mut.next = Box::new(Some(n2)); 

     n1_mut 
    }; 
} 
error[E0382]: use of moved value: `n1_mut` 
    --> src/main.rs:44:9 
    | 
39 |    next: Box::new(Some(n1_mut)), 
    |         ------ value moved here 
... 
44 |   n1_mut 
    |   ^^^^^^ value used here after move 
    | 
    = note: move occurs because `n1_mut` has type `MyRecordRec2<'_>`, which does not implement the `Copy` trait 
+0

有趣。不知道這是否算作一個錯誤 - 我不認爲你可以引發不安全因爲之後沒有辦法讀取內存。但是如果你保留一個原始指針到堆棧,你可以知道'n1Mut.next'確實被設置了:https://play.rust-lang.org/?gist=d41422bfd142c289667e7c2fb3183be0&version=undefined – trentcl

+0

有趣的是,這是不可能的之後使用'n1_mut.next'。另外,添加一個'Drop'實現會導致:「錯誤[E0383]:部分重新初始化未初始化的結構'n1_mut'」 –

回答

8

這沒有什麼關係是一個指針或沒有;這個工程,以及:

#[derive(Debug)] 
struct NonCopy; 

#[derive(Debug)] 
struct Example { 
    name: NonCopy, 
} 

fn main() { 
    let mut foo = Example { 
     name: NonCopy, 
    }; 

    drop(foo); 

    foo.name = NonCopy; 
} 

雖然我不能找到相似的,所以問題,我知道我以前見過,這quote from nikomatsakis這樣描述的:

In general moves are tracked at a pretty narrow level of granularity. We intend to eventually permit you to "fill" both fields back in and then use the structure again. I guess that doesn't work today. I have to go look again at the moves code, but I think in general one of the things I'd like to pursue post 1.0 is extending the type system to deal better with things that have been moved from (in particular I want to support moves out of &mut pointers, so long as you restore the value before doing anything fallible). Anyway I think this example more-or-less falls out of treating things in a general way, though you could imagine rules that say "if you move f, you can never again touch any subfields of f without restoring f as a unit".

還有討論the Rust subreddit,這鏈接到Rust issue 21232: "borrow-checker allows partial reinit of struct that has been moved away, but no use of it"

從概念上講,除了結構本身之外,結構中的每個字段都有一個標誌 - 我喜歡想象Chris Morgan's cardboard box analogy。您可以使用結構之前,只要你在回遷搬出一個擁有結構的領域:

drop(foo.name); 
foo.name = NonCopy; 

println!("{:?}", foo); 

顯然,從2014年起,沒有人費心去付出努力,使這標誌着整個結構是有效的再次填補領域。

實際上,您並不需要此功能,因爲您可以一次指定整個變量。目前的實施過於安全,因爲Rust阻止你做一些看起來不錯的事情。