---
title: "Scoped Threads em Rust: thread::scope"
url: "https://rustlang.com.br/stdlib/scoped-threads/"
markdown_url: "https://rustlang.com.br/stdlib/scoped-threads.MD"
description: "Guia completo de scoped threads em Rust: thread::scope, empréstimo de dados locais em threads, concorrência estruturada e processamento paralelo."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# Scoped Threads em Rust: thread::scope

Guia completo de scoped threads em Rust: thread::scope, empréstimo de dados locais em threads, concorrência estruturada e processamento paralelo.


## O que faz e quando usar

`thread::scope` (estável desde Rust 1.63) permite criar threads que podem **referenciar dados da stack** da thread que as criou — sem precisar de `Arc`, `clone` ou `'static`. Isso é possível porque `thread::scope` garante que todas as threads criadas dentro do escopo **terminam antes** que a função retorne, implementando o conceito de **concorrência estruturada** (structured concurrency).

```
  Sem scope (thread::spawn):         Com scope (thread::scope):
  ┌─────────────────────┐            ┌──────────────────────────┐
  │ let dados = vec![..] │            │ let dados = vec![..]     │
  │                       │            │ thread::scope(|s| {      │
  │ // Precisa de Arc     │            │   s.spawn(|| &dados);   │◄─ borrow OK!
  │ // ou move             │            │   s.spawn(|| &dados);   │◄─ borrow OK!
  │ thread::spawn(move || │            │ }); // espera todas     │
  │   // owns dados        │            │ // dados ainda vivo aqui│
  └────────────────────────┘            └──────────────────────────┘
```

Use `thread::scope` quando:

- Você quer que threads **emprestem** dados locais sem transferir ownership.
- Não quer o overhead cognitivo e de performance de `Arc<T>`.
- Quer garantir que todas as threads terminam antes de continuar (structured concurrency).
- Está fazendo **processamento paralelo** de dados em uma fatia (slice) ou vetor.

---

## Tipos e Funções Principais

| Item | Descrição |
|---|---|
| `thread::scope(f)` | Cria um escopo; `f` recebe um `&Scope` |
| `scope.spawn(f)` | Cria uma thread dentro do escopo |
| `ScopedJoinHandle<T>` | Handle para o resultado da thread escopo |
| `handle.join()` | Aguarda a thread terminar (chamado automaticamente no fim do escopo) |

**Importante:** Ao final de `thread::scope`, todas as threads são **automaticamente joined**. Se qualquer thread entrar em panic, o panic é propagado para a thread chamadora.

---

## Exemplos de Código

### Empréstimo básico de dados da stack

```rust
use std::thread;

fn main() {
    let numeros = vec![1, 2, 3, 4, 5];
    let mensagem = String::from("processando");

    // thread::scope garante que as threads terminam antes de retornar
    thread::scope(|s| {
        // Esta thread pode emprestar &numeros — sem Arc, sem move!
        s.spawn(|| {
            let soma: i32 = numeros.iter().sum();
            println!("{}: soma = {}", mensagem, soma);
        });

        // Outra thread também pode emprestar os mesmos dados
        s.spawn(|| {
            let max = numeros.iter().max().unwrap();
            println!("{}: máximo = {}", mensagem, max);
        });
    }); // Todas as threads são joined aqui automaticamente

    // numeros e mensagem ainda estão disponíveis!
    println!("Dados originais: {:?}", numeros);
    println!("Mensagem: {}", mensagem);
}
```

### Comparação: spawn vs scope

```rust
use std::sync::Arc;
use std::thread;

fn main() {
    let dados = vec![10, 20, 30, 40, 50];

    // COM thread::spawn — precisa de Arc + clone
    {
        let dados = Arc::new(dados.clone());

        let d1 = Arc::clone(&dados);
        let h1 = thread::spawn(move || {
            println!("spawn: soma = {}", d1.iter().sum::<i32>());
        });

        let d2 = Arc::clone(&dados);
        let h2 = thread::spawn(move || {
            println!("spawn: len = {}", d2.len());
        });

        h1.join().unwrap();
        h2.join().unwrap();
    }

    // COM thread::scope — borrow direto, sem Arc
    {
        thread::scope(|s| {
            s.spawn(|| {
                println!("scope: soma = {}", dados.iter().sum::<i32>());
            });

            s.spawn(|| {
                println!("scope: len = {}", dados.len());
            });
        });
    }

    // Muito mais simples e eficiente!
}
```

### Escrita concorrente com fatias mutáveis

Uma das maiores vantagens de scoped threads: dividir um slice mutável entre threads sem locks:

```rust
use std::thread;

fn main() {
    let mut dados = vec![0u64; 12];

    // Dividir o vetor em fatias mutáveis — cada thread escreve na sua fatia
    thread::scope(|s| {
        for (i, chunk) in dados.chunks_mut(3).enumerate() {
            s.spawn(move || {
                for (j, valor) in chunk.iter_mut().enumerate() {
                    *valor = (i * 3 + j) as u64 * 10;
                }
                println!("Thread {} preencheu {:?}", i, chunk);
            });
        }
    });

    println!("Resultado: {:?}", dados);
    // [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110]
}
```

### Processamento paralelo de dados

```rust
use std::thread;

fn processar_item(item: &str) -> usize {
    // Simular processamento custoso
    std::thread::sleep(std::time::Duration::from_millis(50));
    item.len()
}

fn main() {
    let itens = vec![
        "Rust", "Brasil", "Concorrência", "Segurança",
        "Performance", "Threads", "Escopo", "Paralelo",
    ];

    let resultados: Vec<usize> = thread::scope(|s| {
        let handles: Vec<_> = itens
            .iter()
            .map(|item| {
                s.spawn(move || processar_item(item))
            })
            .collect();

        handles
            .into_iter()
            .map(|h| h.join().unwrap())
            .collect()
    });

    for (item, resultado) in itens.iter().zip(resultados.iter()) {
        println!("{}: {} chars", item, resultado);
    }
}
```

### Acessando múltiplas referências

```rust
use std::thread;

struct DadosApp {
    usuarios: Vec<String>,
    config: Config,
}

struct Config {
    max_threads: usize,
    timeout_ms: u64,
}

fn main() {
    let app = DadosApp {
        usuarios: vec![
            "Alice".into(), "Bruno".into(), "Carla".into(),
        ],
        config: Config {
            max_threads: 4,
            timeout_ms: 5000,
        },
    };

    thread::scope(|s| {
        // Thread 1: lê usuários
        s.spawn(|| {
            for user in &app.usuarios {
                println!("Usuário: {}", user);
            }
        });

        // Thread 2: lê config (pode ler ao mesmo tempo — é referência imutável)
        s.spawn(|| {
            println!(
                "Config: max_threads={}, timeout={}ms",
                app.config.max_threads, app.config.timeout_ms
            );
        });
    });

    // app ainda disponível
    println!("Total de usuários: {}", app.usuarios.len());
}
```

### Retornando valores de scoped threads

```rust
use std::thread;

fn main() {
    let dados = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    // scope pode retornar um valor
    let (soma, produto) = thread::scope(|s| {
        let h_soma = s.spawn(|| -> i64 {
            dados.iter().map(|x| *x as i64).sum()
        });

        let h_produto = s.spawn(|| -> i64 {
            dados.iter().map(|x| *x as i64).product()
        });

        let soma = h_soma.join().unwrap();
        let produto = h_produto.join().unwrap();
        (soma, produto)
    });

    println!("Soma: {}, Produto: {}", soma, produto);
    // Soma: 55, Produto: 3628800
}
```

### Tratando panics em scoped threads

```rust
use std::thread;

fn main() {
    let resultado = std::panic::catch_unwind(|| {
        thread::scope(|s| {
            s.spawn(|| {
                println!("Thread normal executando");
            });

            s.spawn(|| {
                panic!("Ops! Thread em pânico!");
            });

            s.spawn(|| {
                println!("Outra thread executando");
            });
        });
        // Se qualquer thread entrou em panic, scope propaga o panic aqui
    });

    match resultado {
        Ok(()) => println!("Todas as threads completaram com sucesso"),
        Err(_) => println!("Pelo menos uma thread entrou em pânico!"),
    }
}
```

---

## Padrões Comuns e Anti-padrões

### Padrão: map-reduce paralelo com scope

```rust
use std::thread;

fn main() {
    let dados: Vec<u64> = (1..=1_000_000).collect();
    let num_threads = 8;
    let chunk_size = dados.len() / num_threads;

    let soma_total: u64 = thread::scope(|s| {
        let handles: Vec<_> = dados
            .chunks(chunk_size)
            .map(|chunk| {
                s.spawn(|| -> u64 {
                    chunk.iter().sum() // borrow direto do chunk!
                })
            })
            .collect();

        handles
            .into_iter()
            .map(|h| h.join().unwrap())
            .sum()
    });

    println!("Soma: {}", soma_total);
    // Soma: 500000500000
}
```

### Anti-padrão: usar scope onde spawn seria melhor

```rust
use std::thread;
use std::time::Duration;

fn main() {
    // ERRADO: usar scope para threads de longa duração que não precisam
    // acessar dados locais — scope bloqueia até todas terminarem!
    //
    // thread::scope(|s| {
    //     s.spawn(|| {
    //         loop { /* servidor rodando para sempre */ }
    //     });
    // }); // Nunca retorna!

    // CORRETO: usar spawn para threads independentes de longa duração
    let _handle = thread::spawn(|| {
        // Background worker
        thread::sleep(Duration::from_secs(1));
    });

    // CORRETO: usar scope para trabalho paralelo de curta duração
    let dados = vec![1, 2, 3, 4, 5];
    thread::scope(|s| {
        for chunk in dados.chunks(2) {
            s.spawn(|| {
                println!("Processando: {:?}", chunk);
            });
        }
    });
}
```

### Anti-padrão: tentar mover dados para fora do scope

```rust
use std::thread;

fn main() {
    let dados = vec![1, 2, 3];

    thread::scope(|s| {
        // ERRO: não pode mover dados para dentro de uma scoped thread
        // se o dado é emprestado por outra thread no mesmo escopo

        // Isso funciona (apenas uma thread usa o dado com move):
        // s.spawn(move || {
        //     println!("{:?}", dados);
        // });

        // Isso funciona (múltiplas threads com referência):
        s.spawn(|| {
            println!("Thread 1: {:?}", &dados);
        });
        s.spawn(|| {
            println!("Thread 2: {:?}", &dados);
        });
    });

    println!("Ainda disponível: {:?}", dados);
}
```

---

## Garantias de Thread Safety

- `thread::scope` garante que **todas** as threads criadas terminam antes de retornar (**structured concurrency**).
- Threads dentro do scope podem emprestar dados com lifetimes menores que `'static` — diferente de `thread::spawn`.
- Se qualquer thread entrar em panic, `scope` aguarda as demais e então propaga o panic.
- `chunks_mut()` com scoped threads é a forma segura de escrever em fatias diferentes de um vetor em paralelo, sem locks.
- A closure passada para `scope.spawn()` precisa ser `Send` (os dados capturados devem ser transferíveis entre threads).

---

## Veja Também

- [std::thread em Rust](/stdlib/thread/) — `thread::spawn` para threads `'static`
- [Mutex e RwLock em Rust](/stdlib/mutex/) — proteger dados quando scope não basta
- [Send e Sync](/stdlib/send-sync/) — traits necessárias para dados em threads
- [Padrões de Thread Safety](/stdlib/thread-safety-patterns/) — quando usar scope vs Arc
- [Concorrência em Rust](/tutoriais/concorrencia/) — tutorial completo de concorrência
- Documentação oficial: [`std::thread::scope`](https://doc.rust-lang.org/std/thread/fn.scope.html)
