2016-10-27 7 views
10

C++(間違っていると私を修正してください)では、定数参照による一時的なバインディングは、それがバインドされている式よりも長生きすることになっています。私はRustにも同じことが当てられていると仮定しましたが、私は2つの異なる場合に2つの異なる行動を取る。Rustのテンポラリを含む破壊命令

は考えてみましょう:

struct A; 
impl Drop for A { fn drop(&mut self) { println!("Drop A.") } } 

struct B(*const A); 
impl Drop for B { fn drop(&mut self) { println!("Drop B.") } } 

fn main() { 
    let _ = B(&A as *const A); // B is destroyed after this expression itself. 
} 

出力は次のようになります。これは、あなたが期待するものです

Drop B. 
Drop A. 

fn main() { 
    let _b = B(&A as *const A); // _b will be dropped when scope exits main() 
} 

を出力は次のとおりです:あなたがしなければしかし、今

Drop A. 
Drop B. 

これは私が期待したものではありません。

これが意図されていますか?その場合、2つの場合の動作の違いの根拠は何ですか?

私は錆1.12.1を使用しています。

+0

ここにいくつかの議論がありますが、それでもまだわかりませんが、前よりも一貫しています。https://github.com/rust-lang/rust/issues/32433 – WiSaGaN

答えて

5

一時停止は、C++の場合と同様に、文の最後にドロップされます。しかし、IIRCでは、Rustの破壊の順序は不明です(これについては後述します)。現在の実装では単純に逆の順序で値を落としているようです。

let _ = x;let _b = x;の間に大きな違いがあります。 _はRustの識別子ではありません。ワイルドカードパターンです。このパターンは変数を検出しないため、最終値はステートメントの最後に効果的に削除されます。

一方、_bは識別子であるため、値はその名前の変数にバインドされます。この値は、関数の終わりまで有効です。しかし、Aインスタンスはまだ一時的なので、ステートメントの最後に削除されます(C++は同じことをすると思います)。ステートメントの終わりは関数の終わりより前に来るので、Aインスタンスは最初に削除され、Bインスタンスは2番目に削除されます。

これをより明確にするために、のはmainで別のステートメントを追加してみましょう。

fn main() { 
    let _ = B(&A as *const A); 
    println!("End of main."); 
} 

これは、次のような出力生成します。これまでのところは良い

Drop B. 
Drop A. 
End of main. 

を。さあ、let _bで試してみましょう。出力は次のとおりです。

Drop A. 
End of main. 
Drop B. 

私たちが見ることができるように、Drop BEnd of main.後に印刷されます。これは、関数の最後までBインスタンスが生きていて、異なる破壊順序を説明していることを示しています。


それでは、私たちが代わりに生のポインタの寿命を借りポインタを取るためにBを変更した場合に何が起こるか見てみましょう。実際には、のは、さらに一歩進み、一瞬Drop実装を削除してみましょう:これは罰金コンパイル

struct A; 
struct B<'a>(&'a A); 

fn main() { 
    let _ = B(&A); 
} 

。背後では、RustはAインスタンスとBインスタンスの両方に同じ寿命を割り当てます(つまり、Bインスタンスへの参照を取得した場合、そのタイプは&'a B<'a>となります)。の場合、両方とも'aは完全に同じです。 2つの値が同じ寿命を持つ場合、必然的にそれらの一方を他方よりも先に落とす必要があり、上述のように、順序は不特定である。 Dropの実装を追加するとどうなりますか? AインスタンスとBインスタンスの両方が同じ寿命が割り当てられているので

error: borrowed value does not live long enough 
--> <anon>:8:16 
    | 
8 |  let _ = B(&A); 
    |    ^does not live long enough 
    | 
note: reference must be valid for the destruction scope surrounding statement at 8:4... 
--> <anon>:8:5 
    | 
8 |  let _ = B(&A); 
    |  ^^^^^^^^^^^^^^ 
note: ...but borrowed value is only valid for the statement at 8:4 
--> <anon>:8:5 
    | 
8 |  let _ = B(&A); 
    |  ^^^^^^^^^^^^^^ 
help: consider using a `let` binding to increase its lifetime 
--> <anon>:8:5 
    | 
8 |  let _ = B(&A); 
    |  ^^^^^^^^^^^^^^ 

、錆がこれらのオブジェクトの破壊の順序について推論することはできません。

struct A; 
impl Drop for A { fn drop(&mut self) { println!("Drop A.") } } 

struct B<'a>(&'a A); 
impl<'a> Drop for B<'a> { fn drop(&mut self) { println!("Drop B.") } } 

fn main() { 
    let _ = B(&A); 
} 

は、今、私たちは、コンパイラエラーを取得しています。このエラーは、B<'a>Drop(このルールはRust 1.0より前にRFC 769の結果として追加されています)を実装したときに、Rustがオブジェクト自体の有効期間でB<'a>をインスタンス化することを拒否したことに起因します。許可されている場合は、dropはすでに削除された値にアクセスできます。ただし、B<'a>Dropが実装されていない場合は、構造体が破棄されるときにコードがBのフィールドにアクセスしようとしないことがわかっているため、許可されています。

+0

https: //github.com/rust-lang/rfcs/pull/769これは、ドロップタイプの「厳密に大きなライフタイム」を導入したものです。 – Neikos

5

生は、コンパイラは、このような何かを行う可能性がありますので、自身が生涯の任意の並べ替えを実行していないポインタ:

  1. 例:それは*const A内に保持することができるように

    • Bが(作成されますそれは、結合に結合していないので、落ちてしまう。
    • Aが必要とされていないので、

を落としますのは、MIRをチェックしてみましょう:

fn main() ->() { 
    let mut _0:();      // return pointer 
    let mut _1: B; 
    let mut _2: *const A; 
    let mut _3: *const A; 
    let mut _4: &A; 
    let mut _5: &A; 
    let mut _6: A; 
    let mut _7:(); 

    bb0: { 
     StorageLive(_1);     // scope 0 at <anon>:8:13: 8:30 
     StorageLive(_2);     // scope 0 at <anon>:8:15: 8:29 
     StorageLive(_3);     // scope 0 at <anon>:8:15: 8:17 
     StorageLive(_4);     // scope 0 at <anon>:8:15: 8:17 
     StorageLive(_5);     // scope 0 at <anon>:8:15: 8:17 
     StorageLive(_6);     // scope 0 at <anon>:8:16: 8:17 
     _6 = A::A;      // scope 0 at <anon>:8:16: 8:17 
     _5 = &_6;      // scope 0 at <anon>:8:15: 8:17 
     _4 = &(*_5);      // scope 0 at <anon>:8:15: 8:17 
     _3 = _4 as *const A (Misc);  // scope 0 at <anon>:8:15: 8:17 
     _2 = _3;       // scope 0 at <anon>:8:15: 8:29 
     _1 = B::B(_2,);     // scope 0 at <anon>:8:13: 8:30 
     drop(_1) -> bb1;     // scope 0 at <anon>:8:31: 8:31 
    } 

    bb1: { 
     StorageDead(_1);     // scope 0 at <anon>:8:31: 8:31 
     StorageDead(_2);     // scope 0 at <anon>:8:31: 8:31 
     StorageDead(_3);     // scope 0 at <anon>:8:31: 8:31 
     StorageDead(_4);     // scope 0 at <anon>:8:31: 8:31 
     StorageDead(_5);     // scope 0 at <anon>:8:31: 8:31 
     drop(_6) -> bb2;     // scope 0 at <anon>:8:31: 8:31 
    } 

    bb2: { 
     StorageDead(_6);     // scope 0 at <anon>:8:31: 8:31 
     _0 =();       // scope 0 at <anon>:7:11: 9:2 
     return;       // scope 0 at <anon>:9:2: 9:2 
    } 
} 

私たちは、このようにあなたが上記の出力を取得し、推定されるようdrop(_1)が実際drop(_6)前に呼び出され見ることができるように。

  • A(上記と同じ理由で)作成された結合

    • Bに結合している。この例Bでは
      1. Aはバインドされずにドロップされます。
      2. Bが範囲外になり、ドロップされます。

    対応するMIR:

    fn main() ->() { 
        let mut _0:();      // return pointer 
        scope 1 { 
         let _1: B;      // "b" in scope 1 at <anon>:8:9: 8:10 
        } 
        let mut _2: *const A; 
        let mut _3: *const A; 
        let mut _4: &A; 
        let mut _5: &A; 
        let mut _6: A; 
        let mut _7:(); 
    
        bb0: { 
         StorageLive(_1);     // scope 0 at <anon>:8:9: 8:10 
         StorageLive(_2);     // scope 0 at <anon>:8:15: 8:29 
         StorageLive(_3);     // scope 0 at <anon>:8:15: 8:17 
         StorageLive(_4);     // scope 0 at <anon>:8:15: 8:17 
         StorageLive(_5);     // scope 0 at <anon>:8:15: 8:17 
         StorageLive(_6);     // scope 0 at <anon>:8:16: 8:17 
         _6 = A::A;      // scope 0 at <anon>:8:16: 8:17 
         _5 = &_6;      // scope 0 at <anon>:8:15: 8:17 
         _4 = &(*_5);      // scope 0 at <anon>:8:15: 8:17 
         _3 = _4 as *const A (Misc);  // scope 0 at <anon>:8:15: 8:17 
         _2 = _3;       // scope 0 at <anon>:8:15: 8:29 
         _1 = B::B(_2,);     // scope 0 at <anon>:8:13: 8:30 
         StorageDead(_2);     // scope 0 at <anon>:8:31: 8:31 
         StorageDead(_3);     // scope 0 at <anon>:8:31: 8:31 
         StorageDead(_4);     // scope 0 at <anon>:8:31: 8:31 
         StorageDead(_5);     // scope 0 at <anon>:8:31: 8:31 
         drop(_6) -> [return: bb3, unwind: bb2]; // scope 0 at <anon>:8:31: 8:31 
        } 
    
        bb1: { 
         resume;       // scope 0 at <anon>:7:1: 9:2 
        } 
    
        bb2: { 
         drop(_1) -> bb1;     // scope 0 at <anon>:9:2: 9:2 
        } 
    
        bb3: { 
         StorageDead(_6);     // scope 0 at <anon>:8:31: 8:31 
         _0 =();       // scope 1 at <anon>:7:11: 9:2 
         drop(_1) -> bb4;     // scope 0 at <anon>:9:2: 9:2 
        } 
    
        bb4: { 
         StorageDead(_1);     // scope 0 at <anon>:9:2: 9:2 
         return;       // scope 0 at <anon>:9:2: 9:2 
        } 
    } 
    

    我々はdrop(_6)drop(_1)前に呼び出されるん見ることができるように、我々はあなたが見てきた動作を得ます。

  • 関連する問題