อาร์กิวเมนต์ของฟังก์ชันเริ่มต้นใน Rust


102

เป็นไปได้ไหมที่ Rust จะสร้างฟังก์ชันที่มีอาร์กิวเมนต์เริ่มต้น

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

4
# 6973มีวิธีแก้ปัญหาหลายอย่าง (โดยใช้โครงสร้าง)
huon

ในปี 2020 คุณจะเขียนโค้ดได้อย่างไร?
puentesdiaz

@puentesdias คำตอบที่ยอมรับยังคงเป็นคำตอบที่ถูกต้อง ไม่มีทางที่จะทำมันใน Rust ไม่เป็นและคุณมีทั้งเขียนแมโครหรือการใช้งานและชัดเจนผ่านOption None
Jeroen

คำตอบ:


55

ไม่มันไม่ได้อยู่ในปัจจุบัน ฉันคิดว่ามันน่าจะถูกนำมาใช้ในที่สุด แต่ปัจจุบันยังไม่มีงานที่ทำอยู่ในพื้นที่นี้

เทคนิคทั่วไปที่ใช้ในที่นี้คือการใช้ฟังก์ชันหรือวิธีการที่มีชื่อและลายเซ็นต่างกัน


2
@ ner0x652: แต่โปรดทราบว่าแนวทางดังกล่าวไม่ได้รับการสนับสนุนอย่างเป็นทางการ
Chris Morgan

@ChrisMorgan คุณมีแหล่งที่มาของการท้อแท้อย่างเป็นทางการหรือไม่?
Jeroen

1
@JeroenBollen สิ่งที่ดีที่สุดที่ฉันสามารถหาได้ในสองสามนาที 'การค้นหาคือreddit.com/r/rust/comments/556c0g/…ซึ่งคุณมีคนอย่าง brson ที่เป็นหัวหน้าโครงการ Rust ในเวลานั้น IRC อาจมีมากกว่านี้ไม่แน่ใจ
Chris Morgan

108

เนื่องจากไม่รองรับอาร์กิวเมนต์เริ่มต้นคุณจะได้รับพฤติกรรมที่คล้ายกันโดยใช้ 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);

ความแตกต่างใหญ่ระหว่างสองโซลูชันคือเมื่อมีอาร์กิวเมนต์ "Option" -al จะสามารถเขียนได้อย่างสมบูรณ์add(None, Some(4))แต่ด้วยรูปแบบมาโครที่ตรงกันคุณไม่สามารถทำได้ (ซึ่งคล้ายกับกฎอาร์กิวเมนต์เริ่มต้นของ Python)

คุณยังสามารถใช้โครงสร้าง "อาร์กิวเมนต์" และ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)));
}

เห็นได้ชัดว่าตัวเลือกนี้มีโค้ดมากกว่า แต่ต่างจากการออกแบบมาโครที่ใช้ระบบ type ซึ่งหมายความว่าข้อผิดพลาดของคอมไพเลอร์จะเป็นประโยชน์กับผู้ใช้ไลบรารี / API ของคุณมากกว่า นอกจากนี้ยังช่วยให้ผู้ใช้สามารถFromนำไปใช้งานได้ด้วยตนเองหากเป็นประโยชน์กับพวกเขา


2
คำตอบนี้จะดีกว่าเนื่องจากคำตอบหลาย ๆ คำตอบสำหรับแต่ละแนวทาง ฉันต้องการโหวตให้คะแนนเพียงหนึ่งในนั้น
joel

56

ไม่ Rust ไม่รองรับอาร์กิวเมนต์ของฟังก์ชันเริ่มต้น คุณต้องกำหนดวิธีการต่างๆด้วยชื่อที่แตกต่างกัน ไม่มีฟังก์ชันโอเวอร์โหลดเช่นกันเนื่องจาก 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

Rust ไม่รองรับอาร์กิวเมนต์ของฟังก์ชันเริ่มต้นและฉันไม่เชื่อว่าจะมีการใช้งานในอนาคต ดังนั้นฉันจึงเขียน proc_macro duangเพื่อใช้งานในรูปแบบมาโคร

ตัวอย่างเช่น:

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

หากคุณใช้ Rust 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);
}

7
ในขณะที่ความถูกต้องทางเทคนิคชุมชน Rust ถูกแบ่งออกอย่างชัดเจนว่านี่เป็นแนวคิด "ดี" ผมเองตกอยู่ในค่าย "ไม่ดี"
Shepmaster

1
@Shepmaster สามารถเพิ่มขนาดโค้ดได้และไม่สามารถอ่านได้มาก พวกนั้นคัดค้านการใช้รูปแบบนั้นหรือไม่? จนถึงตอนนี้ฉันพบว่าการแลกเปลี่ยนมีความคุ้มค่าในการให้บริการ API ตามหลักสรีรศาสตร์ แต่คิดว่าฉันอาจพลาด gotcha อื่น ๆ
Squidpickles

2

อีกวิธีหนึ่งคือการประกาศ enum โดยมีพารามิเตอร์เสริมเป็นตัวแปรซึ่งสามารถกำหนดพารามิเตอร์เพื่อใช้ประเภทที่เหมาะสมสำหรับแต่ละตัวเลือกได้ ฟังก์ชันนี้สามารถนำไปใช้เพื่อแบ่งส่วนความยาวตัวแปรของตัวแปร enum สามารถเรียงลำดับและความยาวได้ ค่าเริ่มต้นจะถูกนำไปใช้ภายในฟังก์ชันเป็นการกำหนดเบื้องต้น

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.