2015-11-11 9 views
8

私はチャネルを介して関数を送信する方法と、もう一方の端で関数を実行するために余分なクローンを避ける方法を考えています。私はクロージャ内の余分なクローニング操作を削除した場合、私は次のエラーを取得する:`Fn`クロージャーでキャプチャされた外部変数から移動できません

error: cannot move out of captured outer variable in an 'Fn' closure 

は、このコードは絶対に何もしませんし、グローバル可変静Sender<T>を利用し、それは私は何を表しているという事実を無視します適切なコンパイラエラーを与えながら達成しようとしています。このコードはであり、実行されることを意図していません。、ちょうどコンパイルされています。

use std::ops::DerefMut; 
use std::sync::{Arc, Mutex}; 
use std::collections::LinkedList; 
use std::sync::mpsc::{Sender, Receiver}; 

type SafeList = Arc<Mutex<LinkedList<u8>>>; 
type SendableFn = Arc<Mutex<(Fn() + Send + Sync + 'static)>>; 
static mut tx: *mut Sender<SendableFn> = 0 as *mut Sender<SendableFn>; 

fn main() { 
    let list: SafeList = Arc::new(Mutex::new(LinkedList::new())); 
    loop { 
     let t_list = list.clone(); 
     run(move || { 
      foo(t_list.clone()); 
     }); 
    } 
} 

fn run<T: Fn() + Send + Sync + 'static>(task: T) { 
    unsafe { 
     let _ = (*tx).send(Arc::new(Mutex::new(task))); 
    } 
} 

#[allow(dead_code)] 
fn execute(rx: Receiver<SendableFn>) { 
    for t in rx.iter() { 
     let mut guard = t.lock().unwrap(); 
     let task = guard.deref_mut(); 
     task(); 
    } 
} 

#[allow(unused_variables)] 
fn foo(list: SafeList) { } 

このエラーを回避するにはより良い方法がありますか、チャンネルを介して機能を送信する別の方法がありますか?

答えて

11

Fn()の問題は、複数回呼び出すことができることです。キャプチャされた値から移動した場合、その値は次の呼び出しでもう使用できなくなります。クロージャーを呼び出すことも確実に行うには、FnOnce()が必要です。そのため、クロージャーは消えてしまい、再度呼び出すことはできません。

Arc<Mutex<(FnOnce() + Send + Sync + 'static)>>の方法はありません。これは、関数を呼び出した後で誰もそれを再度呼び出すことができないことを静的に保証する必要があります。他の誰かがあなたのFnOnceを指している別のArcを持っている可能性があります。あなたができることはそれを箱に入れて、それをBox<FnOnce() + Send + Sync + 'static>として送ることです。 Boxの所有者は1人だけです。

FnOnce()の問題は、Boxにある間は実際には呼び出せないということです。それはBoxから移動して呼び出す必要があるからです。しかし、我々はそれのサイズを知らないので、我々はBoxからそれを移動することはできません。将来的にはBox<FnOnce()>クロージャが直接使用可能になるかもしれません。

「幸いにも」この問題はより頻繁に発生したので、FnBoxがあります。残念ながら、これは夜間に働くことを必要とします。また、私はドキュメントで説明されている関数呼び出しの構文を使用する方法を理解できませんでしたが、Box<FnBox()>で手動でcall_boxを呼び出すことができます。 Playground

+0

残念ながら、安定性は私の唯一の選択肢です。あなたがすでに試してみたことを確認したように見えますが、他の誰かが夜間に使う能力を持つこのスーパーの人を助けてくれるかもしれません。ありがとう:) – nathansizemore

+1

ハックを使用することができます。 'Fn'を使い、' take 'で移動する 'Option'sで動くだけです。 'Fn'を誤って使用したときにランタイムエラーが発生する –

+0

ライブラリ要件のために' Fn'が必要な場合はどうすればいいですか? –