재귀

내부적으로, async fn.await하는 하위 Future를 갖는 상태기계 타입을 만듭니다. 때문에, async fn을 재귀적으로 사용하기 살짝 까다롭습니다. 왜냐하면, 결과를 도출할 상태기계 타입이 그 자신을 포함해야 하기 때문입니다.


#![allow(unused)]
fn main() {
async fn step_one() { /* ... */ }
async fn step_two() { /* ... */ }
struct StepOne;
struct StepTwo;
// This function:
async fn foo() {
    step_one().await;
    step_two().await;
}
// generates a type like this:
enum Foo {
    First(StepOne),
    Second(StepTwo),
}

// So this function:
async fn recursive() {
    recursive().await;
    recursive().await;
}

// generates a type like this:
enum Recursive {
    First(Recursive),
    Second(Recursive),
}
}

위 예제는 무한한 크기의 타입을 만들기 때문에 작동하지 않습니다. 컴파일러 오류는 다음과 같습니다.

error[E0733]: recursion in an `async fn` requires boxing
 --> src/lib.rs:1:22
  |
1 | async fn recursive() {
  |                      ^ an `async fn` cannot invoke itself directly
  |
  = note: a recursive `async fn` must be rewritten to return a boxed future.

제대로 작동하게 하기 위해서는, Box를 이용해 우회접근해야 합니다. 불행하게도, 컴파일러의 제한에 따라 Box::pin으로 recursive() 호출을 감싸는 것만으로는 충분하지 않습니다. 제대로 하려면은, 아래 예제처럼 recursive.boxed()된 비 async 블록 안에 넣어야 합니다.


#![allow(unused)]
fn main() {
use futures::future::{BoxFuture, FutureExt};

fn recursive() -> BoxFuture<'static, ()> {
    async move {
        recursive().await;
        recursive().await;
    }.boxed()
}
}