포스트

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

Implementing function overloading using structs and traits.

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

개요

유감스럽게도 러스트엔 함수 오버로딩(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));