2016-06-03 5 views
6

मैं पारित फ़ंक्शन की "जेनेरिकनेस" को खोए बिना किसी अन्य फ़ंक्शन (इस मामले में बंद) में एक सामान्य फ़ंक्शन पास करने में सक्षम होना चाहता हूं। ,तर्क के रूप में जेनेरिक फ़ंक्शन पास करें

use std::fmt::Debug; 

fn test<F, I: Debug>(gen: F) where F: Fn(fn(I) -> I) -> I { 
    fn input<I: Debug>(x: I) -> I { 
     x 
    } 

    println!("{:?}", gen(input)); 
} 

fn main() { 
    test(|input| { 
     input(10); 
     input(10.0) 
    }); 
} 

यह संकलन नहीं करेगा क्योंकि input का मूल्य inferenced टाइप और अब सामान्य है: चूंकि कि एक बहुत जटिल बयान है, यहाँ एक उदाहरण है।

पूर्ण त्रुटि:

<anon>:14:15: 14:19 error: mismatched types: 
expected `_`, 
    found `_` 
(expected integral variable, 
    found floating-point variable) [E0308] 
<anon>:14   input(10.0) 
         ^~~~ 

जंग में ऐसी बात संभव है?

संपादित करें:

समाधान के आधार पर दिया जाता है, मैं का उपयोग किया है एक ऐसी ही समस्या का समाधान करने के लिए निम्न:

#![feature(unboxed_closures)] 
#![feature(fn_traits)] 

use std::ops::Fn; 
use std::ops::Add; 
use std::ops::FnMut; 

use std::fmt::Debug; 

struct Builder; 

impl Builder { 
    pub fn build<A: Add<B>, B: Add<A>>(&self) -> fn(A, B) -> <A as std::ops::Add<B>>::Output { 
     fn c<A: Add<B>, B: Add<A>>(a: A, b: B) -> <A as std::ops::Add<B>>::Output { 
      a + b 
     } 

     return c; 
    } 
} 

impl<A: Add<B>, B: Add<A>> Fn<(A, B)> for Builder { 
    extern "rust-call" fn call(&self, args: (A, B)) -> <A as std::ops::Add<B>>::Output { 
     let (a1, a2) = args; 
     self.build()(a1, a2) 
    } 
} 

impl<A: Add<B>, B: Add<A>> FnMut<(A, B)> for Builder { 
    extern "rust-call" fn call_mut(&mut self, args: (A, B)) -> <A as std::ops::Add<B>>::Output { 
     let (a1, a2) = args; 
     self.build()(a1, a2) 
    } 
} 

impl<A: Add<B>, B: Add<A>> FnOnce<(A, B)> for Builder { 
    type Output = <A as std::ops::Add<B>>::Output; 
    extern "rust-call" fn call_once(self, args: (A, B)) -> <A as std::ops::Add<B>>::Output { 
     let (a1, a2) = args; 
     self.build()(a1, a2) 
    } 
} 

fn test<F, I: Debug>(gen: F) where F: Fn(Builder) -> I { 
    let b = Builder; 
    println!("{:?}", gen(b)); 
} 

fn main() { 
    test(|builder| { 
     builder(10, 10); 
     builder(10.1, 10.0) 
    }); 
} 
+0

जंग खेल का मैदान लिंक: https://play.rust-lang.org/?कोड = उपयोग% 20std% 3A% 3Afmt% 3A% 3ADebug% 3 बी% 0A% 0Afn% 20test% 3CF% 2C% 20I% 3A% 20Debug% 3E (जनरल% 3A% 20F)% 20where% 20F% 3A% 20Fn (fn (आई)% 20% 3E% 20I)% 20% 3E% 20I% 20% 7B% 0A% 20% 20% 20% 20fn% 20input% 3CI% 3A% 20Debug% 3E (एक्स% 3A% 20I)% 20% 3E% 20I% 20% 7B% 0A% 20% 20% 20% 20% 20% 20% 20% 20x% 0A% 20% 20% 20% 20% 7 दिन% 0A% 20% 20% 20% 20 % 0A% 20% 20% 20% 20println! (% 22% 7B% 3A% 3F% 7 दिन% 22% 2C% 20gen (इनपुट))% 3 बी% 0A% 7 दिन% 0A% 0Afn% 20main()% 20% 7B % 0A% 20% 20% 20% 20test (% 7Cinput% 7C% 20% 7B% 0A% 20% 20% 20% 20% 20% 20% 20% 20input (10)% 3 बी% 0A% 20% 20% 20 % 20% 20% 20% 20% 20 इनपुट (10.0)% 0 ए% 20% 20% 20% 20% 7 डी)% 3 बी% 0 ए% 7 डी और संस्करण = स्थिर और बैकट्रैक = 0 – dpzmick

+1

मुझे यकीन है कि यह कभी काम नहीं करेगा? आप 'test' को कॉल कर रहे हैं जिसे 'जीन' बंद करने के पहले कॉल की अनुमान के आधार पर u32 होने के लिए monomorphized किया जाएगा। यदि आप एक अलग चाहते हैं .. आपको फिर से 'टेस्ट' कॉल करना होगा .. अलग से। मैं इसे अपने सिर में "संकलित" कर रहा हूं और मैं नहीं देखता कि यह या किसी अन्य भाषा में कैसे संभव होगा, क्योंकि अनुमान कम हो जाएगा। शायद मैं गलत हूँ हालांकि। –

उत्तर

7

जैसा कि उल्लेख किया गया है, दुर्भाग्यवश कॉल कॉल पर मोनोमोर्फिज्ड है, इसलिए आप एक सामान्य कार्य नहीं पारित कर सकते हैं, आप केवल सामान्य कार्य का एक मोनोमोर्फिज्ड संस्करण पास कर सकते हैं।

क्या आप कर सकते हैं पास, हालांकि, एक समारोह बिल्डर है:

use std::fmt::Debug; 

struct Builder; 

impl Builder { 
    fn build<I: Debug>(&self) -> fn(I) -> I { 
     fn input<I: Debug>(x: I) -> I { x } 
     input 
    } 
} 

fn test<F, T: Debug>(gen: F) 
    where F: Fn(Builder) -> T 
{ 
    let builder = Builder; 
    println!("{:?}", gen(builder)); 
} 

fn main() { 
    test(|builder| { 
     builder.build()(10); 
     builder.build()(10.0) 
    }); 
} 

Builder मांग पर input के उदाहरण उत्पन्न करने में सक्षम है।

2

बहुत दिलचस्प सवाल! मुझे यकीन है कि यह ऐसा नहीं है।

जंग जेनरिक मोनोमोर्फिंग कार्यों द्वारा काम करते हैं। इसका मतलब यह है कि जंग कम्पाइलर फ़ंक्शन के मशीन कोड को प्रत्येक ठोस प्रकार के फ़ंक्शन के साथ बुलाएगा। फ़ंक्शन के एक कॉल के भीतर, सामान्य पैरामीटर निश्चित होते हैं। इसलिए जब आप test को पर main पर कॉल करते हैं, तो सामान्य कॉलम उस कॉल के लिए तय किए जाते हैं।

इसका तात्पर्य है कि बंद करने का प्रकार तय किया गया है और बंद होने के input पैरामीटर में भी ठोस प्रकार है। संकलक हमारे लिए सभी प्रकार deduces, लेकिन अगर हम इन पर टिप्पणी करने की कोशिश करेगा, हम जल्दी से लगता है कि हम संकलक रूप में एक ही समस्या में पड़:

test::<_, usize> // we can't ever spell out a closure type, therefore '_' 
    (|input: fn(usize) -> usize| // we can't have a generic closure right now 
{ 
    input(10); // works 
    input(10.0) // doesn't work 
}); 

यह एक उपयोग के मामले की तरह एक बहुत लग रहा है उच्च प्रकार के प्रकार और सामान्य बंद करने के लिए। दोनों सुविधाएं अभी तक जंग में उपलब्ध नहीं हैं, AFAIK।

हालांकि, अगर आप अभी भी प्राप्त कर सकते हैं गतिशील प्रेषण का उपयोग करके आप क्या चाहते हैं:

fn test<F, I: Debug>(gen: F) where F: Fn(fn(Box<Debug>) -> Box<Debug>) -> I { 
    fn input(x: Box<Debug>) -> Box<Debug> { 
     x 
    } 

    println!("{:?}", gen(input)); 
} 

fn main() { 
    test(|input| { 
     input(Box::new(10)); 
     input(Box::new(10.0)) 
    }); 
} 
बेशक

, इस जेनेरिक वर्जन के रूप में के रूप में अच्छा नहीं है, लेकिन कम से कम यह काम करता है। इसके अलावा: अगर आपको वास्तव में input में स्वामित्व की आवश्यकता नहीं है, तो आप Box<Debug> से &Debug बदल सकते हैं।

+0

यह एक अच्छा जवाब है। मैंने दूसरा जवाब स्वीकार कर लिया है क्योंकि मेरा समाधान आपके उत्तर के मुकाबले उतना ही समान है, लेकिन आप इस मुद्दे का स्पष्टीकरण बहुत अच्छा है। धन्यवाद! – dpzmick

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