[0]

Rust의 비동기 프로그래밍을 위한 async/await 키워드와 Future trait에 대해 알아본다. 

 

[1]

Rust는 async/await, 그리고 이와 관련한 Future trait을 통해 비동기를 지원한다. 다음과 같이 간단히 사용할 수 있다. Future는 trait으로 다음과 같은 간단히 선언되어 있다. 

pub trait Future {
    type Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}

trait 자체는 Output type과 poll메소드로 매우 간단하다. poll에서는 Context라는 구조체를 변수로 받는데, 이에 대해서는 나중에 설명하도록 하겠다. async 키워드를 이용해 다음과 같이 Future trait object를 만들 수 있다.

async fn worker(){
    println!("this is worker!");
}

async{
    println("this is anonymous async!")
}

함수 선언에 async 키워드를 붙이거나, async 블록을 이용해서도 Future object를 만들 수 있다. 이 코드들은 일반적인 방식으로는 실행할 수 없다. 예를 들어 worker()로 호출하더라도 아무 의미 없는 구문일 뿐이다. 이를 실행하기 위해서는 await을 사용해야 한다. 

async fn worker(){
    println!("this is worker!");
}

fn main() {
    worker().await;
}

error[E0728]: `await` is only allowed inside `async` functions and blocks
  --> src\main.rs:10:5
   |
5  | fn main() {
   |    ---- this is not `async`
...
10 |     worker().await;
   |     ^^^^^^^^^^^^^^ only allowed inside `async` functions and blocks

error: aborting due to previous error

하지만 Future object에 무작정 await를 하면 위와 같이 await는 async 블록 내에서만 사용할 수 있다는 에러가 발생한다. 그럼 main을 async로 바꿔보자.

error[E0752]: `main` function is not allowed to be `async`
 --> src\main.rs:5:1
  |
5 | async fn main() {
  | ^^^^^^^^^^^^^^^ `main` function is not allowed to be `async`

error: aborting due to 2 previous errors

main은 async로 선언할 수 없다는 에러를 뱉는다. 그럼 임의로 async 블록을 만들고 그 안에서 await해보자.

async fn worker(){
    println!("this is worker!");
}

fn main() {
    async{
        worker().await;
    };
}

 

위와 같은 코드는 컴파일 가능하지만, "this is worker"가 출력되지는 않는다. main 내의 async블록이 await되지 않았기 때문이다. 정리해보자면 await를 하기 위해선 async 내부여야 하고, main은 async로 선언될 수 없다. 따라서 main이 아닌 최상위의 async 블록이 필요한데, 그 최상위의 async를 실행시킬 수가 없다! 이를 해결하기 위해서는 별도의 executor가 필요하다.

 

[2]

여기에서는 가장 널리 사용되는 tokio를 사용하겠다. 

use tokio::prelude::*;

async fn worker(){
    println!("this is worker!");
}

#[tokio::main]
async fn main() {
    worker().await;
    tokio::task::spawn(worker());
}

 

Cargo.toml에 tokio를 full feature로 포함시키면 위의 코드를 실행시킬 수 있다. worker().await또는 직접 task::spawn()을 사용하여 async 블록을 실행할 수 있다. 하지만 tokio는 꽤나 복잡하기 때문에 async가 어떻게 돌아가는지 알기는 다소 어렵다. 다음 포스팅에서 executor를 직접 구현하면서 async의 동작에 대해 알아보자. 

 

[참고]

https://rust-lang.github.io/async-book/

 

Getting Started - Asynchronous Programming in Rust

Welcome to Asynchronous Programming in Rust! If you're looking to start writing asynchronous Rust code, you've come to the right place. Whether you're building a web server, a database, or an operating system, this book will show you how to use Rust's asyn

rust-lang.github.io

 

'Computer Science > Rust' 카테고리의 다른 글

Unsafe Rust  (0) 2020.09.27
Rust의 trait object  (0) 2020.08.02
Rust의 Copy trait와 Clone trait  (2) 2020.06.30
Rust의 스마트 포인터  (0) 2020.05.20
Rust의 lifetime parameter  (0) 2020.05.05

+ Recent posts