간편한 함수 오버로딩 구현하기
포스트
취소

간편한 함수 오버로딩 구현하기

개요

유감스럽게도 러스트엔 함수 오버로딩(overloading), default parameter, optional parameter 등이 없습니다.
하지만 구조체를 사용하여, 오버로딩의 흉내는 낼 수 있습니다.

1
2
3
4
5
6
struct Overloading;

trait Foo<T> {
    type Output;
    fn ctor(arg: T) -> Self::Output;
}

이렇게 선언된 구조체와 트레잇을 이용하여 함수 오버로딩을 사용할 수 있습니다:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
impl Foo<usize> for Overloading {
    type Output = usize;

    fn ctor(arg: usize) -> Self::Output {
        arg * 10
    }
}

impl Foo<String> for Overloading {
    type Output = String;

    fn ctor(arg: String) -> Self::Output {
        arg + "!"
    }
}

ctorconstructor를 의미합니다. 이 예제에선 ctor 라는 네이밍을 사용했습니다.

이런 식으로 제네릭 T엔 인자 타입, 연관 타입(associated type) Output을 구현하여, 오버로딩을 흉내 낼 수 있습니다.
이제 헬퍼(Helper) 함수를 이용해서 편리하게 호출할 수 있습니다:

1
2
3
4
5
6
7
8
9
10
11
12
#[inline]
fn foo<T>(arg: T) -> <Overloading as Foo<T>>::Output
where
    Overloading: Foo<T>,
{
    <Overloading as Foo<T>>::ctor(arg)
}

fn main() {
    println!("{}", foo(2));
    println!("{}", foo(String::from("Hello")));
}

번외로, 여기서 #[inline] 속성이 사용되었습니다. 이에 대한 글은 이곳을 참고해봅시다.

다만 복수 개의 인자를 받을 수는 없습니다. 그럴땐 튜플을 사용하거나 매크로를 사용해봅시다:

1
2
3
4
5
6
7
8
9
impl Foo<(usize, usize)> for Overloading {
    type Output = usize;

    fn ctor(arg: (usize, usize)) -> Self::Output {
        arg.0 + arg.1
    }
}

println!("{}", foo((2, 3)));
1
2
3
4
5
6
7
8
9
10
macro_rules! foo {
    ($arg:expr) => {
        $arg * 10
    };
    ($a:expr, $b:expr) => {
        $a + $b
    };
}

assert_eq!(foo!(2), foo!(15, 5));