डिफ़ॉल्ट फ़ंक्शन तर्क जंग में


100

क्या एक डिफ़ॉल्ट तर्क के साथ फ़ंक्शन बनाने के लिए रस्ट में यह संभव है?

fn add(a: int = 1, b: int = 2) { a + b }

4
# 6973 में कई वर्क-अराउंड (एक संरचना का उपयोग करके) शामिल हैं।
हून

2020 में, आप इसे कैसे कोड कर सकते हैं?
प्यूएंटेसिडिया

@puentesdias स्वीकृत उत्तर अभी भी सही उत्तर है। रस्ट में इसे करने का कोई तरीका नहीं है, और आपको या तो एक मैक्रो लिखना होगा, या उपयोग करना होगा Optionऔर स्पष्ट रूप से पास करना होगा None
जेरेन

जवाबों:


55

नहीं, यह वर्तमान में नहीं है। मुझे लगता है कि संभावना है कि यह अंततः लागू हो जाएगा, लेकिन वर्तमान में इस स्थान में कोई सक्रिय काम नहीं है।

यहां कार्यरत विशिष्ट तकनीक विभिन्न नामों और हस्ताक्षरों के साथ कार्यों या विधियों का उपयोग करना है।


2
@ ner0x652: लेकिन ध्यान दें कि दृष्टिकोण आधिकारिक तौर पर हतोत्साहित किया गया है।
क्रिस मॉर्गन

@ क्रिसमोरन क्या आपके पास आधिकारिक रूप से हतोत्साहित होने का स्रोत है?
Jeroen

1
@JeroenBollen सबसे अच्छा मैं कुछ मिनटों की खोज के साथ आ सकता है reddit.com/r/rust/comments/556c0g/… , जहां आपके पास ब्रॉन जैसे लोग हैं जो उस समय रस्ट प्रोजेक्ट लीडर थे। IRC और अधिक हो सकता है, निश्चित नहीं था।
क्रिस मॉर्गन

107

चूंकि डिफ़ॉल्ट तर्क समर्थित नहीं हैं, इसलिए आप एक समान व्यवहार का उपयोग कर सकते हैं Option<T>

fn add(a: Option<i32>, b: Option<i32>) -> i32 {
    a.unwrap_or(1) + b.unwrap_or(2)
}

यह डिफ़ॉल्ट मान और फ़ंक्शन को केवल एक बार (प्रत्येक कॉल के बजाय) कोडित करने के उद्देश्य को पूरा करता है, लेकिन निश्चित रूप से टाइप करने के लिए एक पूरी बहुत अधिक है। फ़ंक्शन कॉल ऐसा दिखेगा add(None, None), जिसे आप अपने दृष्टिकोण के आधार पर पसंद कर सकते हैं या नहीं।

यदि आप तर्क सूची में कुछ भी नहीं टाइप करते हुए देखते हैं क्योंकि कोडर संभावित रूप से एक विकल्प बनाना भूल जाता है, तो यहां बड़ा फायदा खोजबीन में है; फोन करने वाला स्पष्ट रूप से कह रहा है कि वे आपके डिफ़ॉल्ट मूल्य के साथ जाना चाहते हैं, और यदि वे कुछ भी नहीं डालते हैं, तो उन्हें एक संकलन त्रुटि मिलेगी। इसे टाइपिंग समझें add(DefaultValue, DefaultValue)

आप एक मैक्रो का उपयोग भी कर सकते हैं:

fn add(a: i32, b: i32) -> i32 {
    a + b
}

macro_rules! add {
    ($a: expr) => {
        add($a, 2)
    };
    () => {
        add(1, 2)
    };
}
assert_eq!(add!(), 3);
assert_eq!(add!(4), 6);

दो समाधानों के बीच बड़ा अंतर यह है कि "विकल्प" -al तर्कों के साथ यह लिखना पूरी तरह से मान्य है add(None, Some(4)) , लेकिन मैक्रो पैटर्न से मेल खाने से आप नहीं कर सकते (यह पायथन के डिफ़ॉल्ट तर्क नियमों के समान है)।

आप "तर्क" संरचना और From/ Intoलक्षण का भी उपयोग कर सकते हैं:

pub struct FooArgs {
    a: f64,
    b: i32,
}

impl Default for FooArgs {
    fn default() -> Self {
        FooArgs { a: 1.0, b: 1 }
    }
}

impl From<()> for FooArgs {
    fn from(_: ()) -> Self {
        Self::default()
    }
}

impl From<f64> for FooArgs {
    fn from(a: f64) -> Self {
        Self {
            a: a,
            ..Self::default()
        }
    }
}

impl From<i32> for FooArgs {
    fn from(b: i32) -> Self {
        Self {
            b: b,
            ..Self::default()
        }
    }
}

impl From<(f64, i32)> for FooArgs {
    fn from((a, b): (f64, i32)) -> Self {
        Self { a: a, b: b }
    }
}

pub fn foo<A>(arg_like: A) -> f64
where
    A: Into<FooArgs>,
{
    let args = arg_like.into();
    args.a * (args.b as f64)
}

fn main() {
    println!("{}", foo(()));
    println!("{}", foo(5.0));
    println!("{}", foo(-3));
    println!("{}", foo((2.0, 6)));
}

यह विकल्प स्पष्ट रूप से बहुत अधिक कोड है, लेकिन मैक्रो डिज़ाइन के विपरीत यह टाइप सिस्टम का उपयोग करता है जिसका अर्थ है कि कंपाइलर त्रुटियां आपके लाइब्रेरी / एपीआई उपयोगकर्ता के लिए अधिक सहायक होंगी। यह भी उपयोगकर्ताओं को अपने स्वयं के Fromकार्यान्वयन करने की अनुमति देता है यदि यह उनके लिए उपयोगी है।


2
यह उत्तर कई उत्तरों के रूप में बेहतर होगा, प्रत्येक दृष्टिकोण के लिए एक। मैं उनमें से सिर्फ एक को उभारना चाहता हूं
joel

56

नहीं, Rust डिफ़ॉल्ट फंक्शन आर्ग्युमेंट्स का समर्थन नहीं करता है। आपको अलग-अलग तरीकों को अलग-अलग नामों से परिभाषित करना होगा। कोई फ़ंक्शन ओवरलोडिंग भी नहीं है, क्योंकि जंग प्रकारों को प्राप्त करने के लिए फ़ंक्शन नाम का उपयोग करती है (फ़ंक्शन ओवरलोडिंग के लिए इसके विपरीत की आवश्यकता होती है)।

स्ट्रक्चर इनिशियलाइज़ेशन के मामले में आप इस तरह से स्ट्रक्चर अपडेट सिंटैक्स का उपयोग कर सकते हैं:

use std::default::Default;

#[derive(Debug)]
pub struct Sample {
    a: u32,
    b: u32,
    c: u32,
}

impl Default for Sample {
    fn default() -> Self {
        Sample { a: 2, b: 4, c: 6}
    }
}

fn main() {
    let s = Sample { c: 23, .. Sample::default() };
    println!("{:?}", s);
}

[अनुरोध पर, मैंने एक नकली प्रश्न से इस उत्तर को पार कर दिया]


4
यह डिफ़ॉल्ट तर्कों के लिए एक बहुत प्रयोग करने योग्य पैटर्न है। उच्चतर होना चाहिए
बेन

9

जंग डिफ़ॉल्ट फ़ंक्शन तर्कों का समर्थन नहीं करती है, और मुझे विश्वास नहीं है कि इसे भविष्य में लागू किया जाएगा। इसलिए मैंने इसे स्थूल रूप में लागू करने के लिए एक proc_macro युगल लिखा ।

उदाहरण के लिए:

duang! ( fn add(a: i32 = 1, b: i32 = 2) -> i32 { a + b } );
fn main() {
    assert_eq!(add!(b=3, a=4), 7);
    assert_eq!(add!(6), 8);
    assert_eq!(add(4,5), 9);
}

7

यदि आप 1.12 रस्ट या बाद का उपयोग कर रहे हैं, तो आप कम से कम फ़ंक्शन तर्कों को उपयोग करने में आसान बना सकते हैं Optionऔर into():

fn add<T: Into<Option<u32>>>(a: u32, b: T) -> u32 {
    if let Some(b) = b.into() {
        a + b
    } else {
        a
    }
}

fn main() {
    assert_eq!(add(3, 4), 7);
    assert_eq!(add(8, None), 8);
}

6
तकनीकी रूप से सटीक होते हुए, रस्ट समुदाय को "अच्छा" विचार है या नहीं, इस पर शाब्दिक रूप से विभाजित किया गया है। मैं व्यक्तिगत रूप से "अच्छा नहीं" शिविर में आता हूं।
Shepmaster

1
@ शेमेस्टर यह संभवतः कोड आकार बढ़ा सकता है, और यह सुपर पठनीय नहीं है। क्या उस पैटर्न का उपयोग करने में आपत्तियां हैं? मैंने अब तक एर्गोनोमिक एपीआई की सेवा में व्यापार-नापसंद को सार्थक पाया है, लेकिन इस पर विचार करेगा कि मुझे कुछ अन्य गोचरों की याद आ रही है।
स्क्विडपिकल्स

2

एक और तरीका यह हो सकता है कि वैरिएंट के रूप में वैकल्पिक परम के साथ एक एनम घोषित किया जाए, जिसे प्रत्येक विकल्प के लिए सही प्रकार लेने के लिए पैरामीटर किया जा सकता है। फ़ंक्शन को एनम वेरिएंट की एक वैरिएबल लंबाई स्लाइस लेने के लिए लागू किया जा सकता है। वे किसी भी क्रम और लंबाई में हो सकते हैं। फ़ंक्शन को प्रारंभिक असाइनमेंट के रूप में डिफ़ॉल्ट रूप से कार्यान्वित किया जाता है।

enum FooOptions<'a> {
    Height(f64),
    Weight(f64),
    Name(&'a str),
}
use FooOptions::*;

fn foo(args: &[FooOptions]) {
    let mut height   = 1.8;
    let mut weight   = 77.11;
    let mut name     = "unspecified".to_string();

    for opt in args {
        match opt {
            Height(h) => height = *h,
            Weight(w) => weight = *w,
            Name(n)   => name   =  n.to_string(),
        }
    }
    println!("  name: {}\nweight: {} kg\nheight: {} m", 
             name, weight, height);
}

fn main() { 

            foo( &[ Weight(90.0), Name("Bob") ] );

}

उत्पादन:

  name: Bob
weight: 90 kg
height: 1.8 m
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.