Future is Not Send — Como Resolver no Rust

Como resolver o erro 'future is not Send' no Rust async. Entenda por que Rc, RefCell e MutexGuard causam este erro e aprenda a usar Arc, Mutex e técnicas corretas.

Future is Not Send

O erro “future is not Send” é um dos mais confusos da programação assíncrona em Rust. Ele ocorre quando você tenta enviar um future para outra thread (como ao usar tokio::spawn), mas o future contém tipos que não são seguros para compartilhar entre threads.

A Mensagem de Erro

error: future cannot be sent between threads safely
   --> src/main.rs:10:5
    |
10  |     tokio::spawn(async {
    |     ^^^^^^^^^^^^ future created by async block is not `Send`
    |
    = help: within `{async block}`, the trait `Send` is not implemented for `Rc<String>`
note: future is not `Send` as this value is used across an await
   --> src/main.rs:12:9
    |
11  |     let dados = Rc::new(String::from("olá"));
    |         ----- has type `Rc<String>` which is not `Send`
12  |     alguma_funcao().await;
    |                    ^^^^^^ await occurs here, with `dados` maybe used later

O Que Significa

No Rust, o trait Send indica que um tipo pode ser transferido com segurança entre threads. Quando você usa tokio::spawn (ou equivalentes em outros runtimes async), o future é movido para uma thread de trabalho. Para isso ser seguro, tudo dentro do future precisa ser Send.

Tipos que NÃO são Send:

TipoPor que não é SendAlternativa Send
Rc<T>Contagem de referência não-atômicaArc<T>
RefCell<T>Empréstimo verificado em runtime, não thread-safeMutex<T> ou RwLock<T>
Cell<T>Mutabilidade interior não thread-safeAtomicXxx ou Mutex<T>
MutexGuard<T>Lock guard mantido através de .awaitSoltar guard antes do await
Ponteiros raw *const T / *mut TSem garantias de segurançaEncapsular em tipo Send

O problema não é apenas ter esses tipos, mas mantê-los vivos através de um .await. O compilador precisa garantir que, se o runtime pausar o future no .await e continuar em outra thread, todos os dados serão válidos.

Código com Erro

Rc através de await

use std::rc::Rc;

#[tokio::main]
async fn main() {
    tokio::spawn(async {
        let dados = Rc::new(String::from("compartilhado"));
        fazer_algo().await;  // ERRO: `Rc` vivo através do await
        println!("{}", dados);
    });
}

async fn fazer_algo() {
    tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}

MutexGuard através de await

use std::sync::Mutex;

#[tokio::main]
async fn main() {
    let dados = std::sync::Arc::new(Mutex::new(vec![1, 2, 3]));

    tokio::spawn(async move {
        let guard = dados.lock().unwrap();
        // ERRO: MutexGuard mantido através do await
        fazer_algo().await;
        println!("{:?}", guard);
    });
}

RefCell em contexto async

use std::cell::RefCell;

#[tokio::main]
async fn main() {
    tokio::spawn(async {
        let celula = RefCell::new(42);
        fazer_algo().await;  // ERRO
        println!("{}", celula.borrow());
    });
}

Como Resolver

Solução 1: Substituir Rc por Arc

use std::sync::Arc;

#[tokio::main]
async fn main() {
    tokio::spawn(async {
        let dados = Arc::new(String::from("compartilhado"));
        fazer_algo().await;
        println!("{}", dados);  // Funciona! Arc é Send
    });
}

async fn fazer_algo() {
    tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}

Solução 2: Substituir RefCell por Mutex/RwLock

use std::sync::{Arc, Mutex};

#[tokio::main]
async fn main() {
    let dados = Arc::new(Mutex::new(vec![1, 2, 3]));

    let dados_clone = Arc::clone(&dados);
    tokio::spawn(async move {
        dados_clone.lock().unwrap().push(4);
        fazer_algo().await;
        println!("{:?}", dados_clone.lock().unwrap());
    });
}

Para async, prefira tokio::sync::Mutex que não bloqueia a thread:

use std::sync::Arc;
use tokio::sync::Mutex;

#[tokio::main]
async fn main() {
    let dados = Arc::new(Mutex::new(vec![1, 2, 3]));

    let dados_clone = Arc::clone(&dados);
    tokio::spawn(async move {
        {
            let mut guard = dados_clone.lock().await;
            guard.push(4);
        } // guard liberado antes do await

        fazer_algo().await;
        println!("{:?}", dados_clone.lock().await);
    });
}

Solução 3: Soltar o Guard Antes do await

Se o tipo não-Send é temporário, garanta que ele não cruza o .await:

use std::sync::{Arc, Mutex};

#[tokio::main]
async fn main() {
    let dados = Arc::new(Mutex::new(vec![1, 2, 3]));

    let dados_clone = Arc::clone(&dados);
    tokio::spawn(async move {
        // Escopo limita o guard
        {
            let mut guard = dados_clone.lock().unwrap();
            guard.push(4);
        } // guard destruído aqui — antes do await

        fazer_algo().await;  // OK: nenhum tipo não-Send vivo

        let guard = dados_clone.lock().unwrap();
        println!("{:?}", *guard);
    });
}

Solução 4: Usar spawn_local para Tasks Não-Send

Se você realmente precisa de tipos não-Send, use spawn_local (executa na mesma thread):

use std::rc::Rc;
use tokio::task;

#[tokio::main]
async fn main() {
    let local = task::LocalSet::new();

    local.run_until(async {
        task::spawn_local(async {
            let dados = Rc::new(String::from("local"));
            fazer_algo().await;
            println!("{}", dados);  // OK com spawn_local
        }).await.unwrap();
    }).await;
}

Solução 5: Extrair a Parte Não-Send para uma Closure

use std::sync::Arc;

#[tokio::main]
async fn main() {
    let dados = Arc::new(std::sync::Mutex::new(0));

    let dados_clone = Arc::clone(&dados);
    tokio::spawn(async move {
        // Processa antes do await
        let valor = {
            let guard = dados_clone.lock().unwrap();
            *guard // Copia o valor, guard destruído no fim do bloco
        };

        fazer_algo().await;
        println!("Valor: {}", valor);
    });
}

async fn fazer_algo() {
    tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}

Regra de Ouro

Se o tipo cruza um .await e será usado por tokio::spawn:

  • Rc -> Arc
  • RefCell -> tokio::sync::Mutex ou tokio::sync::RwLock
  • Cell -> std::sync::atomic::AtomicXxx
  • MutexGuard -> solte antes do .await

Veja Também