Rust의 기본 함수 인수


104

Rust에서 기본 인자로 함수를 만드는 것이 가능합니까?

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

4
# 6973 에는 몇 가지 해결 방법이 포함되어 있습니다 (구조체 사용).
후온

2020 년에는 어떻게 코딩 할 수 있습니까?
puentesdiaz

@puentesdias 받아 들여진 대답은 여전히 ​​정답입니다. Rust에서는 할 수있는 방법이 없으며, 매크로를 작성하거나 사용 Option하고 명시 적으로 전달해야 None합니다.
Jeroen

답변:


55

아니요, 현재는 아닙니다. 언젠가는 구현 될 것 같지만 현재이 공간에서는 활발한 작업이 없습니다.

여기에 사용되는 일반적인 기술은 이름과 서명이 다른 함수 나 메서드를 사용하는 것입니다.


2
@ ner0x652 :하지만 그 접근 방식은 공식적으로 권장되지 않습니다.
Chris Morgan

@ChrisMorgan 공식적으로 낙담하는 소스가 있습니까?
Jeroen

1
@JeroenBollen 몇 분 동안 검색 할 수있는 가장 좋은 방법은 reddit.com/r/rust/comments/556c0g/… 인데, 당시 Rust 프로젝트 리더였던 brson과 같은 사람들이 있습니다. IRC는 더 많았을 수도 있지만 확실하지 않았습니다.
Chris Morgan

110

기본 인수가 지원되지 않기 때문에 다음을 사용하여 유사한 동작을 얻을 수 있습니다. 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))하지만 매크로 패턴이 일치하면 불가능하다는 것입니다 (파이썬의 기본 인수 규칙과 유사합니다).

"arguments"구조체와 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)));
}

이 선택은 분명히 훨씬 더 많은 코드이지만 매크로 디자인과 달리 유형 시스템을 사용하므로 컴파일러 오류가 라이브러리 / API 사용자에게 더 도움이 될 것입니다. 이것은 또한 From도움이되는 경우 사용자가 직접 구현할 수 있도록합니다.


3
이 답변은 각 접근 방식에 대해 하나씩 여러 답변으로 더 좋을 것입니다. 나는 그들 중 하나만
joel

57

아니요, 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
이것은 기본 인수에 매우 유용한 패턴입니다. 더 높아야합니다
Ben

10

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 이상을 사용하고 있다면 최소한 함수 인자를 Optionand 와 함께 사용하기 쉽게 만들 수 있습니다 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 서비스에서 절충안이 가치가 있음을 알았지 만 다른 문제를 놓치고 있다고 생각할 것입니다.
squidpickles

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.