2015-01-11 14 views
12

Ein FnMut kann aus offensichtlichen Gründen nicht geklont werden.Können Sie einen Verschluss klonen?

Ein Fn hat jedoch einen unveränderlichen Umfang; Gibt es eine Möglichkeit, ein "Duplikat" eines Fn zu erstellen?

Der Versuch, es zu klonen führt:

&mut core::ops::Fn(logger::Level, &'a collections::string::String) + Send does not implement any method in scope named clone

Oder ist es sicher, um irgendwie einen rohen Zeiger auf eine Fn passieren, wie:

let func_pnt = &mut Box<Fn<...> + Send> as *mut Box<Fn<...>> 

Technisch die oben genannten Arbeiten, aber es scheint, ziemlich seltsam.

Humm, ok, hier ist ein Beispiel dafür, was ich versuche zu tun:

#![feature(unboxed_closures)] 

use std::thread::Thread; 
use std::clone::Clone; 

struct WithCall { 
    fp:Box<Fn<(i8,i8), i8> + Send> 
} 

impl WithCall { 
    pub fn new(fp:Box<Fn<(i8,i8), i8> + Send>) -> WithCall { 
    return WithCall { 
     fp: fp 
    }; 
    } 

    pub fn run(&self, a:i8, b:i8) -> i8 { 
    return self.fp.call((a, b)); 
    } 
} 

unsafe impl Send for WithCall {} 

impl Clone for WithCall { 
    fn clone(&self) -> WithCall { 
    return WithCall { 
     fp: self.fp.clone() 
    }; 
    } 
} 

fn main() { 
    let adder = WithCall::new(Box::new(|&:a:i8, b:i8| -> i8 { 
    return a + b; 
    })); 
    println!("{}", adder.run(1, 2)); 

    let add_a = adder.clone(); 
    let add_b = adder.clone(); 
    Thread::scoped(move || { 
    println!("In remote thread: {}", add_a.run(10, 10)); 
    }); 
    Thread::scoped(move || { 
    println!("In remote thread: {}", add_b.run(10, 10)); 
    }); 
} 

dh. Sie haben eine Struktur mit einem umschlossenen Abschluss, Sie müssen diese Struktur an eine Reihe von Aufgaben übergeben. offensichtlich kannst du nicht. ... aber man kann es auch nicht klonen, weil Sie keinen Box<Fn<>> klonen und Sie können eine &Fn<...>

Laufstall nicht klonen: http://is.gd/1oNPYJ

+0

Was würden Sie mit dem geklonten Schließung zu tun? – Shepmaster

+0

Wie lautet Ihr vollständiger Code? – huon

+0

@shepmaster Ich möchte speziell eine Schließung ohne veränderbaren Zustand klonen, um sie gleichzeitig in mehrere Aufgaben zu verschieben. Siehe das Beispiel, das ich angehängt habe. – Doug

Antwort

1

Denken Sie daran, dass Schließungen ihrer Umgebung erfassen, so dass sie eine Lebenszeit, basierend auf der Umwelt. Sie können jedoch Hinweise auf die Fn* nehmen und die um weiter passieren, oder speichern sie in einer Struktur:

fn do_more<F>(f: &F) -> u8 
    where F: Fn(u8) -> u8 
{ 
    f(0) 
} 

fn do_things<F>(f: F) -> u8 
    where F: Fn(u8) -> u8 
{ 
    // We can pass the reference to our closure around, 
    // effectively allowing us to use it multiple times. 
    f(do_more(&f)) 
} 

fn main() { 
    let val = 2; 
    // The closure captures `val`, so it cannot live beyond that. 
    println!("{:?}", do_things(|x| (x + 1) * val)); 
} 

Ich würde sagen, dass es nicht überall sicher ist, die Fn* zu einem rohen Zeiger zu konvertieren und gebe es um , aufgrund der Lebensdauer Bedenken.

11

Sie versuchen, eine Closure aus mehreren Threads aufzurufen. Das heißt, teilen Sie die Schließung über mehrere Threads. Sobald der Ausdruck "über mehrere Threads teilen" in meinen Sinn kommt, ist mein erster Gedanke to reach for Arc (mindestens bis RFC 458 ist in irgendeiner Form implementiert, wenn & wird über Threads verwendbar werden). Dies ermöglicht sicheren Shared Memory (implementiert Clone, ohne dass der interne Typ Clone sein muss, da Clone nur einen neuen Zeiger auf den gleichen Speicher erstellt), und so können Sie ein einzelnes Fn Objekt verwenden, das in mehreren Threads verwendet wird, keine Notwendigkeit um es zu duplizieren.

Zusammenfassend, setzen Sie Ihre WithCall in eine Arc und klonen Sie das.

#![allow(unstable)] 

use std::thread::Thread; 
use std::sync::Arc; 

type Fp = Box<Fn(i8,i8) -> i8 + Send + Sync>; 

struct WithCall { 
    fp: Fp 
} 

impl WithCall { 
    pub fn new(fp: Fp) -> WithCall { 
     WithCall { fp: fp } 
    } 

    pub fn run(&self, a: i8, b: i8) -> i8 { 
     (*self.fp)(a, b) 
    } 
} 

fn main() { 
    let adder = WithCall::new(Box::new(|&: a: i8, b| a + b)); 
    println!("{}", adder.run(1, 2)); 

    let add_a = Arc::new(adder); 
    let add_b = add_a.clone(); 
    Thread::scoped(move || { 
     println!("In remote thread: {}", add_a.run(10, 10)); 
    }); 
    Thread::scoped(move || { 
     println!("In remote thread: {}", add_b.run(10, 10)); 
    }); 
} 

playpen

Hinweis: Ich habe das unboxed_closures Feature-Gate entfernt haben, indem die empfohlene Zucker Fn(...) -> ... für Typen und direkte () Anrufe (nicht .call verwenden). Außerdem habe ich die unnötige unsafe impl Send entfernt, da Send automatisch implementiert wird, wenn der Inhalt ist. unsafe impl s sind nur erforderlich, wenn der Inhalt nicht standardmäßig Send ist und der Programmierer die konservative Beurteilung des Compilers überschreiben möchte.


Alte Antwort (das ist immer noch relevant): Es ist ziemlich ungewöhnlich, dass ein &mut Fn Merkmal Objekt zu haben, da Fn::call&self nimmt. Die mut ist nicht notwendig, und ich denke, es fügt buchstäblich keine zusätzliche Funktionalität hinzu. Eine &mut Box<Fn()> zu haben fügt einige Funktionalität hinzu, aber es ist auch ungewöhnlich.

Wenn Sie einen & Zeiger anstelle eines &mut Dinge ändern sich natürlich mehr arbeiten (beide mit &Fn und &Box<Fn>). Ohne den eigentlichen Code zu sehen, die Sie verwenden, ist es extrem schwer, genau zu sagen, was Sie tun, aber

fn call_it(f: &Fn()) { 
    (*f)(); 
    (*f)(); 
} 
fn use_closure(f: &Fn()) { 
    call_it(f); 
    call_it(f); 
} 

fn main() { 
    let x = 1i32; 
    use_closure(&|| println!("x is {}", x)); 
} 

(Dies ist zum Teil auf &TCopy sein und auch zum Teil auf Beleihung, es funktioniert mit &mut . auch)

Alternativ können Sie versenken sich über die Schließung, die wahrscheinlich in mehr Situationen funktioniert:

fn foo(f: &Fn()) { 
    something_else(|| f()) 
} 

Obviously a FnMut cannot be cloned, for obvious reasons.

Es gibt keinen Grund, warum FnMut nicht geklont werden kann. Es ist nur eine Struktur mit einigen Feldern (und eine Methode, die &mut self statt &self oder self wie für Fn bzw. FnOnce nimmt). Wenn Sie eine Struktur erstellen und FnMut manuell implementieren, können Sie dafür weiterhin Clone implementieren.

Or is it safe to somehow pass a raw pointer to a Fn around, like:

let func_pnt = &mut Box<Fn<...> + Send> as *mut Box<Fn<...>> 

Technically the above works, but it seems quite weird.

Technisch funktioniert es, wenn Sie vorsichtig sind sind die Aliasing und Anforderungen an die Lebensdauer von Rust, um sicherzustellen, zufrieden ... aber in unsichere Zeiger entscheiden werden Sie diese Last auf sich selbst setzen, nicht den Compiler helfen lassen Sie. Es ist relativ selten, dass die richtige Antwort auf einen Compiler-Fehler unsafe Code ist, anstatt sich in den Fehler einzuarbeiten und den Code zu optimieren, um ihn sinnvoller zu machen (für den Compiler, was oft dazu führt, dass er für Menschen sinnvoller ist)).

+0

Sorry, das war ein bisschen eine Quatschfrage. Ich habe es mit einem Beispiel aktualisiert, was ich versuche zu tun. Das Schließen über eine Schließung könnte eine Lösung sein, aber ich bin mir nicht sicher, wie Sie es tun würden. – Doug

+0

@Doug, ja, Fragen ohne die eigentliche Quelle sind extrem schwierig auf eine sinnvolle Weise zu beantworten. Aktualisiert. – huon

0

Hier ist der Arbeitscode in 1.22.1

Die Absicht, diese Arbeit zu machen ist.

let x = |x| { println!("----{}",x)}; 

let mut y = Box::new(x); 

y.clone(); 

Der ursprüngliche Code, wie oben vorgeschlagen, wurde verwendet.

Ich begann mit dem Klonen eine Fn Schließung.

type Fp = Box<Fn(i8, i8) -> i8 + Send + Sync>; 

bis Ended WithCallArc um Fp in der Struktur Hinzufügen

Play rust : working code Gist : working code in 1.22.1

Verwandte Themen