rustingcrab


Cleuton Sampaio

Veja no GitHub

Coroutines

Coroutines são “funções que podem pausar e continuar depois”. Em vez de rodar até o fim de uma vez, elas podem fazer um yield (entregar um valor parcial), parar ali, e mais tarde retomar exatamente do ponto em que pararam.

Para que servem?

No Rust atual, coroutines ainda são experimentais (nightly): você marca uma closure especial que pode dar yield, e quem consome chama “retomar” para continuar a execução até o próximo yield ou até terminar.

Apesar do nome, as coroutines do Rust se parecem mais com os generators do Python do que com goroutines do Go.

Comparando com Generators (Python) e Goroutines (GO)

Diferenças-chave (em poucas linhas)

Regra de bolso

Tem que usar o toolchain nightly

Como as coroutines ainda são instáveis: exigem #![feature(coroutines, coroutine_trait, yield_expr)] e a anotação #[coroutine], o compilador só aceita na versão nightly do Rust. No Rust estável, feature gates não são permitidos, então o código não compila.

Para instalar e usar o nightly sem “quebrar” seu stable:

rustup toolchain install nightly
rustup default stable            # mantém o stable como padrão
cargo +nightly run               # usa nightly só neste comando

Se preferir não depender de +nightly na linha de comando, fixe o toolchain no repositório com rust-toolchain.toml:

# rust-toolchain.toml
[toolchain]
channel = "nightly"              # ou um snapshot específico, ex.: "nightly-2025-08-17"
components = ["rustc", "cargo", "clippy", "rustfmt"]
profile = "minimal"

Com esse arquivo, dentro do projeto cargo build já usa o nightly automaticamente, enquanto todos os seus outros projetos continuam no stable.

Exemplo

Segue um exemplo claro usando coroutines para gerar primos com o Crivo de Eratóstenes até um limite dado. é um projeto mínimo completo (3 arquivos). funciona no nightly e não mexe no seu stable.

Arquivo: rust-toolchain.toml

[toolchain]
channel = "nightly"
components = ["rustc", "cargo", "clippy", "rustfmt"]
profile = "minimal"

Arquivo: Cargo.toml

[package]
name = "sieve-coroutines"
version = "0.1.0"
edition = "2024"

[dependencies]

Arquivo: src/main.rs

#![feature(coroutines, coroutine_trait, yield_expr, stmt_expr_attributes)]

use std::ops::{Coroutine, CoroutineState};
use std::pin::Pin;

fn main() {
    let limit = 100;

    // coroutine que gera primos até `limit`
    let mut primes = #[coroutine] move || {
        if limit < 2 {
            return ();
        }

        // crivo clássico
        let mut is_prime = vec![true; limit + 1];
        is_prime[0] = false;
        is_prime[1] = false;

        let mut i = 2usize;
        while i * i <= limit {
            if is_prime[i] {
                let mut m = i * i;
                while m <= limit {
                    is_prime[m] = false;
                    m += i;
                }
            }
            i += 1;
        }

        // produz os primos com `yield`
        for p in 2..=limit {
            if is_prime[p] {
                yield p; // suspende e entrega `p`
            }
        }

        // retorno final (sem valor extra)
        ()
    };

    // consome a coroutine até completar
    print!("primes up to {limit}:");
    loop {
        match Pin::new(&mut primes).resume(()) {
            CoroutineState::Yielded(p) => print!(" {p}"),
            CoroutineState::Complete(()) => break,
        }
    }
    println!();
}

como usar: só rodar cargo run dentro desse projeto. o rust-toolchain.toml faz o cargo usar nightly só aqui, sem tocar no seu padrão stable.