[Rust] 트레잇을 통한 함수 오버로딩 구현 (번역)
https://medium.com/@jreem/advanced-rust-using-traits-for-argument-overloading-c6a6c8ba2e17
러스트는 함수나 메서드에 대한 오버로딩을 기본적으로 지원하지 않습니다. 하지만 트레잇을 유연하게 잘 사용한다면 아주 비슷하게는 흉내를 낼 수 있어요.
간단한 HTTP 서버 프레임워크를 작성한다 칩시다. 리스폰스에 노출해서 리스폰스의 body를 설정하는 메서드를 만들겁니다.
리스폰스는 Reader 트레잇 객체를 받아서 body에 저장하므로, Reader를 허용하는 간단한 메서드를 작성할 수 있습니다.
impl** Response **
{
** pub fn set_body**
** (&mut self, reader: Box<Reader + Send>) **
** {**
** self.body = reader;**
** }**
}
이건 아주 쉽지만, 더 나아가기 전에 일반적인 사용사례를 몇개 시도해봅시다.
이건 임의의(arbitrary) reader를 읽어오는겁니다.
res.set_body(Box::new(get_reader()));
이건 파일을 읽어옵니다.
use std::io::File;
res.set_body(
** Box::new(**
** File::open(Path::new("./file.html"))**
** .unwrap()**
** )**
);
문자열 리터럴이나 Vec
use std::io::MemReader;
res.set_body(
** Box::new(**
** MemReader::new(**
** "bytes".as_bytes().to_vec()**
** )**
** )**
);
이 API가 놀라울 정도로 사용이 장황하고, 여러가지 일반적인 상황에서 비직관적이라는 것을 알기 위해서는 더 많은 테스트가 필요하진 않을 겁니다.
특히 꼬박꼬박 붙이는 Bow::new만 해도 상당히... 귀찮고 불편해보이죠.
더 좋은 방법이 있을 거에요.
어떻게 하면 이 문제를 해결할 수 있을까요?
다양한 타입에 대한 리더를 가져오는 복잡도로부터 사용자를 해방시켜주고 싶은데요. 그렇다면 타입에 대한 변환을 수행하는 트레잇을 써볼 수도 있을겁니다.
trait** IntoReader **
{
** type OutReader: Reader;**
** fn into_reader(self) -> Self::OutReader;**
**} **
그러면, 리스폰스 메서드를 이 트레잇에 대한 제너릭으로 바꿔봅시다.
impl** Response **
{
** pub fn set_body(&mut self, data: I)**
** where I: IntoReader, **
** I::OutReader: Send **
** {**
** self.body = Box::new(data.into_reader());**
** }**
}
이제 set_body 메서드는 다양한 타입들을 받아줄 수 있습니다. 그 타입이 IntoReader 트레잇을 구현하기만 했다면요.
이런것도 잘 동작할겁니다.
use std::io::MemReader;
impl** IntoReader for String **
{
** type OutReader = MemReader;**
** fn into_reader(self) -> MemReader **
** {**
** MemReader::new(self.into_bytes())**
** }**
}
impl** IntoReader for Path **
{
** type OutReader = File;**
** fn into_reader(self) -> File **
** {**
** File::open(self).unwrap()**
** }**
}
// 이외에도 Vec
이제 이전의 사용사례들을 다시 테스트해봅시다.
임의의 Reader를 읽어옵니다.
res.set_body(reader);
파일을 읽어옵니다.
res.set_body(Path::new("./file.html"));
문자열 리터럴이나 Vec
res.set_body(data);
이렇듯 트레잇과 약간의 숨겨진 다형성을 사용하는 것으로 지저분한 API를 더 효과적이고 사용을 쉽게 만들어줄 수 있습니다.
이 변환의 결과물은 메서드 오버로딩과 매우 유사하지만 오히려 더 큰 확장성을 가지고 있습니다. 해당 타입에 대해서만 IntoReader를 구현하면 되니까요.
Iron에서는, 극단적인 논리성을 위해 이 트릭을 사용, 이런 리스폰스 편집에 적합한 rust-modifier를 만들었습니다.
이 Modifiers들은 위에서 만들었던 API와 유사하게 body를 설정할 수 있습니다.
뿐만 아니라, 사용자가 다른 모든 종류의 속성(attributes)을 설정할 수 있고, 하위(downstream) 코드에서 임의의 새 Modifiers를 정의할 수 있게 해줍니다.
역주
iron: rust 웹 프레임워크