2016-12-04 5 views
3

に構造体に格納されているスタックに割り当てられたクロージャーを呼び出す:は、私はこのような構造体の閉鎖を格納していますラスト

#[derive(Clone)] 
struct S<'a> { 
    func: &'a FnOnce() -> u32 
} 

fn main() { 
    let s = S { func: &|| 0 }; 
    let val = (s.func)(); 
    println!("{}", val); 
} 

私はコンパイルすると、s.funcは自身を実行するために移動することはできません。私はなぜそれが移動できないのか理解しています(つまり、コンパイル時にサイズが分かりません)が、なぜそれが全く動かないのか分かりません。

error[E0161]: cannot move a value of type std::ops::FnOnce() -> u32: 
the size of std::ops::FnOnce() -> u32 cannot be statically determined 
--> main.rs:8:15 
    | 
8 |  let val = (s.func)(); 
    |    ^^^^^^^^ 

error[E0507]: cannot move out of borrowed content 
--> main.rs:8:15 
    | 
8 |  let val = (s.func)(); 
    |    ^^^^^^^^ cannot move out of borrowed content 

error: aborting due to 2 previous errors 

Box<FnOnce() -> u32>経由)ヒープ上の閉鎖を保存するために、これを解決するため、この唯一の方法は以下のとおりです。

ここでエラーメッセージがですか?なぜクロージャーを呼び出すのがそれを動かすのですか?おそらくそれを呼び出すと、関数自体は変更されません。

答えて

5

FnOnce::call_onceが値でselfをとるため、クロージャが移動されています。この契約は、関数が複数回呼び出されないという保証を強制します。

クロージャを最大で1回呼び出す場合、FnOnceの特性を使用する場合、構造体はそのクロージャの所有権を取得する必要があります(構造体をクロージャタイプで汎用化する必要があります) 。クロージャを呼び出すと、クロージャが構造体から移​​動し、構造体全体が無効になることに注意してください。 OptiontakeFnOnceをラップしてから、Optionのクロージャを呼び出して呼び出す前に回避することができます。

クロージャを複数回呼び出す場合は、クロージャの所有権を取得したくない場合や、クロージャタイプで構造体を汎用化したくない場合は、Fnまたは代わりにFnMutFn::callは、参照によりselfをとり、FnMut::call_mutは、可変参照によりselfを要する。両方とも参照を受け入れるので、それらと共に特性オブジェクトを使用することができます。

+0

これは、読み込みに役立ちます。 structのconstインスタンスを作成したかったので、FnOnceの代わりに 'Fn'を使ってしまいましたが、この全体の応答は全体を理解するのに役立ちます。 – cderwin

1

フランシスが説明したように、クロージャーを宣言するFnOnceは、クロストの最も広いクラスを受け入れるように指示します。そのようなクロージャが呼び出されると、クロージャオブジェクト自体を破棄することによって(コンパイラ自身のcallメソッドに移動させることによって)、コンパイラによってそのようなクロージャが1回だけ呼び出されることが保証されます。

FnOnceを使用して、まだ閉鎖にジェネリックSを持っていないことが可能であるが、閉鎖は、おそらく複数回呼び出すことができないように、それは物事を設定するにはいくつかの作業が必要です。

  • クロージャはOptionに格納する必要があります。したがって、その内容は "盗難"され、OptionNoneに置き換えられます(この部分はクロージャが2回呼び出されないことを保証します)。
  • オプションからクロージャを盗み出して呼び出す方法を知っている(またはクロージャがすでに盗まれた場合は何か他のことをする)特性を発明しました。
  • は、形質オブジェクトへの参照をSに格納します。これにより、同じSタイプは、クロージャタイプでは一般的ではなく、異なるクロージャで機能します。

結果は次のようになります。

trait Callable { 
    fn call_once_safe(&mut self, default: u32) -> u32; 
} 

impl<F: FnOnce() -> u32> Callable for Option<F> { 
    fn call_once_safe(&mut self, default: u32) -> u32 { 
     if let Some(func) = self.take() { 
      func() 
     } else { 
      default 
     } 
    } 
} 

struct S<'a> { 
    func: &'a mut Callable 
} 

impl<'a> S<'a> { 
    pub fn invoke(&mut self) -> u32 { 
     self.func.call_once_safe(1) 
    } 
} 

fn main() { 
    let mut s = S { func: &mut Some(|| 0) }; 
    let val1 = s.invoke(); 
    let val2 = s.invoke(); 
    println!("{} {}", val1, val2); 
} 

閉鎖についての詳細を知っている唯一の場所が特定Option<F>ためCallableの実装である、それぞれの閉鎖のために生成され、&mut Callableのvtableのが参照しますfuncSに初期化するときに作成されるファットポインタです。

関連する問題