2015-01-11 21 views
12

स्पष्ट कारणों से एक एफएनएमट क्लोन नहीं किया जा सकता है।क्या आप बंद कर सकते हैं?

हालांकि, एक एफएन में एक अपरिवर्तनीय गुंजाइश है; क्या एफएन का 'डुप्लिकेट' बनाने का कोई तरीका है?

क्लोन करने के लिए यह परिणाम कोशिश कर रहा है:

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

या यह किसी भी तरह की तरह चारों ओर एक Fn करने के लिए एक कच्चे सूचक पारित करने के लिए, सुरक्षित है:

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

तकनीकी तौर पर ऊपर काम करता है, लेकिन ऐसा लगता है काफी अजीब

#![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)); 
    }); 
} 

अर्थात्:

Humm, ठीक है, यहाँ मैं क्या कर रहा हूँ का एक उदाहरण है। आपके पास एक बॉक्स किए गए बंद होने के साथ एक संरचना है, आपको उस संरचना को कई कार्यों में पारित करने की आवश्यकता है; जाहिर है आप नहीं कर सकते। ... लेकिन आप भी यह क्लोन नहीं कर सकते, क्योंकि आप एक Box<Fn<>> क्लोन नहीं कर सकते हैं और आप एक &Fn<...>

playpen क्लोन नहीं कर सकते हैं: http://is.gd/1oNPYJ

+0

क्या आप क्लोन बंद के साथ करना चाहते हैं में ArcFp चारों ओर जोड़ने समाप्त? – Shepmaster

+0

आपका पूरा कोड क्या है? – huon

+0

@shepmaster मैं विशेष रूप से एक ही समय में कई कार्यों में स्थानांतरित करने के लिए उत्परिवर्तनीय स्थिति के बिना बंद करना चाहता हूं। मैंने जो उदाहरण संलग्न किया है उसे देखें। – Doug

उत्तर

1

याद रखें कि बंद अपने वातावरण पर कब्जा है, इसलिए वे एक है पर्यावरण के आधार पर स्वयं का जीवनकाल। हालांकि, अगर आप Fn* के लिए संदर्भ लेने के लिए और उन लोगों के आगे के आसपास पारित कर सकते हैं, या उन्हें एक struct में स्टोर:

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)); 
} 

मैं कहूँगा कि यह सार्वभौमिक एक कच्चे सूचक को Fn* कन्वर्ट करने के लिए सुरक्षित नहीं है और यह चारों ओर से पारित , जीवन भर की चिंताओं के कारण।

11

जो आप करने की कोशिश कर रहे हैं वह कई थ्रेड से बंद होना है। यही है, एकाधिक धागे में बंद साझा करें। जैसे ही "एकाधिक धागे में साझा" वाक्यांश मेरे दिमाग को पार करता है, मेरा पहला विचार to reach for Arc है (कम से कम RFC 458 कुछ रूपों में लागू किया गया है, जब & धागे में प्रयोग योग्य हो जाएगा)। यह सुरक्षित साझा स्मृति के लिए अनुमति देता है (यह Clone लागू करता है, इसके बिना Clone होने के कारण आंतरिक प्रकार की आवश्यकता होती है, क्योंकि Clone सिर्फ उसी स्मृति में एक नया सूचक बनाता है), और इसलिए आपके पास एक ही Fn ऑब्जेक्ट हो सकता है जो एकाधिक थ्रेड में उपयोग किया जाता है, कोई ज़रूरत नहीं है इसे डुप्लिकेट करने के लिए।

संक्षेप में, को Arc में रखें और उसे क्लोन करें।

#![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

नोट: मैं प्रकार और प्रत्यक्ष () कॉल के लिए सिफारिश की चीनी Fn(...) -> ... का उपयोग कर (.call का उपयोग नहीं) द्वारा unboxed_closures सुविधा गेट हटा दिया है। साथ ही, मैंने अनावश्यक unsafe impl Send को हटा दिया है, क्योंकि Send सामग्री स्वचालित रूप से लागू हो जाती है। unsafe impl एस केवल तभी आवश्यक है जब सामग्री डिफ़ॉल्ट रूप से Send न हों और प्रोग्रामर कंपाइलर के रूढ़िवादी निर्णय को ओवरराइड करना चाहता है।


पुराना जवाब (यह अभी भी प्रासंगिक है): के बाद से Fn::call&self लेता है यह काफी &mut Fn विशेषता वस्तु के लिए असामान्य है। mut आवश्यक नहीं है, और मुझे लगता है कि यह सचमुच शून्य अतिरिक्त कार्यक्षमता जोड़ता है। &mut Box<Fn()> होने से कुछ कार्यक्षमता जुड़ती है, लेकिन यह भी असामान्य है।

आप एक &mut चीजों के बजाय एक & सूचक को बदलते हैं तो स्वाभाविक रूप से अधिक (&Fn और &Box<Fn> दोनों के साथ) काम करेंगे। वास्तविक कोड का उपयोग कर रहे देखे बिना, यह अत्यंत वास्तव में बताने के लिए आप क्या कर रहे कठिन है, लेकिन

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)); 
} 

(यह आंशिक रूप से की वजह से किया जा रहा है &TCopy और भी आंशिक रूप से कारण reborrowing लिए, यह &mut साथ काम करता है । साथ ही)

वैकल्पिक रूप से, आप बंद से अधिक बंद है, जो की संभावना अधिक स्थितियों में काम करता है सकते हैं:

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

Obviously a FnMut cannot be cloned, for obvious reasons.

कोई अंतर्निहित कारण नहीं है FnMut क्लोन नहीं किया जा सकता है, यह &self या self के बजाय और FnOnce क्रमशः 10 के बजाय &mut self लेता है। यदि आप एक संरचना बनाते हैं और मैन्युअल रूप से FnMut लागू करते हैं, तो आप इसके लिए अभी भी Clone लागू कर सकते हैं।

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.

तकनीकी तौर पर यह काम करता है अगर आप जंग के अलियासिंग और आजीवन आवश्यकताओं को सुनिश्चित करने के संतुष्ट हैं सावधान रहे हैं ... लेकिन असुरक्षित संकेत का विकल्प चुनकर आप अपने शरीर पर कि बोझ डाल रहे हैं, संकलक मदद दे नहीं आप। यह अपेक्षाकृत दुर्लभ है कि एक कंपाइलर त्रुटि के लिए सही प्रतिक्रिया unsafe कोड का उपयोग करने के बजाय त्रुटि में डेल करने और कोड को ट्विक करने के लिए कोड को ट्विक करने के लिए है (संकलक के लिए, जो अक्सर मनुष्यों को अधिक समझ में आता है)।

+0

क्षमा करें, यह एक बकवास सवाल था। मैंने इसे एक उदाहरण के साथ अद्यतन किया है जिसे मैं करने की कोशिश कर रहा हूं। बंद होने पर बंद होना एक समाधान हो सकता है, लेकिन मुझे यकीन नहीं है कि आप इसे कैसे करेंगे। – Doug

+0

@Doug, हां, वास्तविक स्रोत के बिना प्रश्न उपयोगी तरीके से जवाब देने में बेहद मुश्किल हैं। अपडेट किया गया। – huon

0

यहाँ 1.22.1

में काम कर कोड आशय यह काम करने के लिए है।

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

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

y.clone(); 

शीर्ष पर सुझाए गए मूल कोड का उपयोग किया गया था।

मैंने Fn क्लोजिंग क्लोनिंग के साथ शुरू किया।

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

struct WithCall

Play rust : working code Gist : working code in 1.22.1

संबंधित मुद्दे

 संबंधित मुद्दे