[Rust] 반복자에게 할 일 더해주기 - Iterator adapters

다른 Iterator(a.k.a 반복자)를 받아 새로운 반복자를 반환하는 함수를 iterator adapter라고 부른다. adapter라는 이름은 GoF의 디자인 패턴 중 하나인 adapter pattern에서 온 말이라고 한다. 그런데 실제로는 adapter pattern보다는 decorator pattern에 해당하기 때문에 이름을 신경쓰면 용도를 헷갈릴 수 있다. 그러니 이름에 대해서는 크게 신경 쓰지 않는 것이 좋다. 이름에 대한 불평은 그만하고 iterator adapter가 무엇을 하는가?

iterator adapter는 반복자가 순회하면서 할 일을 더해준다. 무슨 말인지는 실제 구현체를 보면 쉽게 이해할 수 있을 것이다. map 함수는 대표적인 adapter다. 함수형 언어를 써본 사람이라면 많이 익숙할 map 함수가 반환하는 반복자는 기존의 값을 새로운 값으로 변환한 값을 순회한다.

이 외에도 표준 라이브러리에 이미 다양한 adapter가 구현돼있다. 이중 가장 많이 사용되는 것은 반복문과 같이 사용하기 좋은 adapter들이다. 조건을 만족하는 값만 돌려주는 filter, 지정 된 몇 개의 값만 반환하는 take, 반대로 몇 개의 값은 생략하고 반환하는 skip, 몇 개씩 값을 건너뛰며 순환하는 step_by와 같은 adapter가 대표적인 예시다. 이런 adapter를 반복문과 함께 사용하면 복잡한 조건 처리를 쉽게 표현할 수 있다.

이 외에도 영원히 종료하지 않고 순환하게 만드는 cycle이나, 순환하는 값에는 변환을 주지 않고, 사이드이펙트를 발생시키기만 하는 inspect도 많이 사용된다.

adapter를 사용할 때 주의해야 할 점이 하나 있다. adapter가 반환하는 것 역시 반복자일 뿐이다. 실제로 반복자를 사용하기 전에는 adapter가 지정한 일을 수행하지 않는다. 쉽게 말해 실제로 값이 필요할 때까지 실행을 미루는 lazy evaluation을 한다는 것이다. 따라서 아래와 같은 코드는 inspect가 돌려주는 반복자를 사용하지 않았기 때문에 화면에 아무런 값을 출력하지 않는다.

그렇다면 여기서 의문이 하나 들 수 있다. iterator adapter가 뱉는 반복자가 lazy evaluation을 한다면, adapter가 입력으로 사용한 반복자는 어떤 상태가 될까? 실제로 iterator adapter 개념이 존재하는 다른 언어에서 이는 큰 문제가 되기도 한다. 하지만 다행히도 Rust는 ownership을 이용해 문제가 발생하는 것을 사전에 막아준다. iterator adapter는 반복자를 받아 새 반복자를 반환하는 함수다. 다시 말해 반복자의 소유권을 가져간다. 만약 반복자의 레퍼런스를 가져가는 함수였다면, 반복자를 빌려 새 반복자를 만든다고 했을 것이다. 따라서 adapter의 호출자는 adapter의 인자로 넣어 준 반복자를 다시는 사용하지 못한다.

이번 글에서는 iterator adapter가 무엇인지와 함께 자주 사용되는 adapter를 몇 개만 소개해보았다. 실제로는 이보다 더 많은 adapter가 표준 라이브러리에 구현돼 있다. 처음 봤을 때는 이게 어디에 쓰일까 하는 의문이 들 수 있다. 하지만 지금 만들고 있는 프로그램을 잘 살펴보자. 생각보다 반복자가 사용되는 경우는 의외로 많다. 그리고 그중에는 iterator adapter를 사용하면 코드가 더 깔끔해지는 경우가 많다. 게다가 다른 언어였으면 iterator adapter를 사용함으로써 생기는 문제도 Rust에서는 발생하지 않으니 걱정하지 않아도 좋다.

댓글

이 블로그의 인기 게시물

[C++] enum class - 안전하고 쓰기 쉬운 enum

RAII는 무엇인가

Log Aggregator 비교 - Scribe, Flume, Fluentd, logstash

[Python] cache 데코레이터로 최적화하기

[Web] SpeechSynthesis - TTS API