Rust

Send e Sync em Rust: Segurança de Concorrência em Tempo de Compilação na Prática

7 min de leitura

Send e Sync em Rust: Segurança de Concorrência em Tempo de Compilação na Prática

Send: Transferência Segura Entre Threads é um trait que garante que um valor pode ser movido com segurança entre threads. Quando você implementa , você afirma: "este tipo é seguro para ser transferido para outra thread de execução". A maioria dos tipos em Rust implementa automaticamente, exceto aqueles que contêm referências não-seguras ou ponteiros brutos. Tipos que não implementam incluem (referência compartilhada não-thread-safe) e . Tentar enviar esses para outra thread resultará em erro de compilação, protegendo você de data races em tempo de compilação, não em tempo de execução. Sync: Compartilhamento Seguro Entre Threads garante que referências a um valor podem ser compartilhadas com segurança entre threads. Um tipo é se é . Isso significa que múltiplas threads podem acessar o mesmo valor simultaneamente sem corrupção de dados. (Atomic Reference Counting) permite compartilhamento de propriedade entre threads, e fornece exclusão mútua. Juntos, é simultaneamente e , tornando-o ideal para estado compartilhado thread-safe. O Impacto da Segurança em Tempo de

<h2>Send: Transferência Segura Entre Threads</h2>

<p><code>Send</code> é um trait que garante que um valor pode ser movido com segurança entre threads. Quando você implementa <code>Send</code>, você afirma: &quot;este tipo é seguro para ser transferido para outra thread de execução&quot;. A maioria dos tipos em Rust implementa <code>Send</code> automaticamente, exceto aqueles que contêm referências não-seguras ou ponteiros brutos.</p>

<pre><code class="language-rust">use std::thread;

#[derive(Clone)]

struct Mensagem {

conteudo: String,

}

impl Send for Mensagem {} // Implementação explícita (redundante aqui, só para demonstrar)

fn main() {

let msg = Mensagem {

conteudo: &quot;Olá de outra thread&quot;.to_string(),

};

let handle = thread::spawn(move || {

println!(&quot;Recebido: {}&quot;, msg.conteudo);

});

handle.join().unwrap();

}</code></pre>

<p>Tipos que <strong>não</strong> implementam <code>Send</code> incluem <code>Rc&lt;T&gt;</code> (referência compartilhada não-thread-safe) e <code>Cell&lt;T&gt;</code>. Tentar enviar esses para outra thread resultará em erro de compilação, protegendo você de data races em tempo de compilação, não em tempo de execução.</p>

<h2>Sync: Compartilhamento Seguro Entre Threads</h2>

<p><code>Sync</code> garante que referências a um valor podem ser compartilhadas com segurança entre threads. Um tipo <code>T</code> é <code>Sync</code> se <code>&amp;T</code> é <code>Send</code>. Isso significa que múltiplas threads podem acessar o mesmo valor simultaneamente sem corrupção de dados.</p>

<pre><code class="language-rust">use std::sync::{Arc, Mutex};

use std::thread;

fn main() {

let contador = Arc::new(Mutex::new(0));

let mut handles = vec![];

for _ in 0..5 {

let contador_clone = Arc::clone(&amp;contador);

let handle = thread::spawn(move || {

let mut num = contador_clone.lock().unwrap();

*num += 1;

});

handles.push(handle);

}

for handle in handles {

handle.join().unwrap();

}

println!(&quot;Contador final: {}&quot;, *contador.lock().unwrap());

}</code></pre>

<p><code>Arc&lt;T&gt;</code> (Atomic Reference Counting) permite compartilhamento de propriedade entre threads, e <code>Mutex&lt;T&gt;</code> fornece exclusão mútua. Juntos, <code>Arc&lt;Mutex&lt;T&gt;&gt;</code> é simultaneamente <code>Send</code> e <code>Sync</code>, tornando-o ideal para estado compartilhado thread-safe.</p>

<h2>O Impacto da Segurança em Tempo de Compilação</h2>

<p>A verdadeira revolução de Rust é que estas garantias são verificadas <strong>antes</strong> de você executar uma única linha de código. Linguagens como Java e Python dependem de locks em tempo de execução e testes extensivos. Rust elimina classes inteiras de bugs antes da compilação.</p>

<pre><code class="language-rust">// Este código NÃO compila:

use std::rc::Rc;

use std::thread;

fn main() {

let rc = Rc::new(5);

thread::spawn(move || {

println!(&quot;{}&quot;, rc);

});

// erro: Rc&lt;i32&gt; cannot be sent between threads safely

}

// Solução: usar Arc em vez de Rc

use std::sync::Arc;

fn main() {

let arc = Arc::new(5);

thread::spawn(move || {

println!(&quot;{}&quot;, arc);

}).join().unwrap();

}</code></pre>

<p>O compilador força você a escolher as primitivas corretas desde o início. Não há surpresas em produção: se compila, é seguro. Essa abordagem previne deadlocks sutis, race conditions e memory safety issues que afetam sistemas concorrentes em outras linguagens.</p>

<h2>Traits Automáticos e Casos Avançados</h2>

<h3>Implementação Automática</h3>

<p>Rust implementa <code>Send</code> e <code>Sync</code> <strong>automaticamente</strong> para tipos que atendem aos critérios:</p>

<ul>

<li>Um struct é <code>Send</code> se todos seus campos são <code>Send</code></li>

<li>Um struct é <code>Sync</code> se todos seus campos são <code>Sync</code></li>

</ul>

<pre><code class="language-rust">struct Seguro {

valor: i32, // i32 é Send + Sync

texto: String, // String é Send + Sync

}

// Seguro é automaticamente Send + Sync

struct Inseguro {

ponteiro: const u8, // const não implementa Send

}

// Inseguro NÃO é Send/Sync (risco: dados não-sincronizados)</code></pre>

<h3>Casos Avançados: Negação Explícita</h3>

<p>Ocasionalmente, você quer <strong>impedir</strong> que um tipo seja <code>Send</code> ou <code>Sync</code>, mesmo que todos os campos permitam:</p>

<pre><code class="language-rust">use std::marker::PhantomData;

struct NaoEnviavel {

dados: String,

_phantom: PhantomData&lt;const ()&gt;, // const não é Send

}

// NaoEnviavel agora é !Send mesmo que String seja Send</code></pre>

<p>Isso é útil quando seu tipo encapsula invariantes específicas de thread que não devem ser violadas.</p>

<h2>Conclusão</h2>

<p>Aprendemos que <strong>Send e Sync são os pilares da segurança de concorrência em Rust</strong>. Send garante movimentação segura entre threads, enquanto Sync permite compartilhamento seguro de referências. O brilho dessa abordagem é que essas garantias são <strong>verificadas em tempo de compilação</strong>, eliminando race conditions e deadlocks antes da execução. Use <code>Arc&lt;Mutex&lt;T&gt;&gt;</code> para estado compartilhado, respeite as restrições do compilador, e você construirá sistemas concorrentes robustos e eficientes.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://doc.rust-lang.org/book/ch16-00-concurrency.html" target="_blank" rel="noopener noreferrer">The Rust Programming Language - Fearless Concurrency (Capítulo 16)</a></li>

<li><a href="https://doc.rust-lang.org/std/marker/trait.Send.html" target="_blank" rel="noopener noreferrer">Rust std::marker::Send Documentation</a></li>

<li><a href="https://doc.rust-lang.org/std/marker/trait.Sync.html" target="_blank" rel="noopener noreferrer">Rust std::marker::Sync Documentation</a></li>

<li><a href="https://tokio.rs/" target="_blank" rel="noopener noreferrer">Tokio Runtime - Advanced Concurrency Patterns</a></li>

<li><a href="https://www.youtube.com/watch?v=3AwQoWf6lb8" target="_blank" rel="noopener noreferrer">Jon Gjengset - Crust of Rust: Concurrency (YouTube)</a></li>

</ul>

Comentários

Mais em Rust

Como Usar Workspaces no Cargo: Organizando Projetos Grandes em Rust em Produção
Como Usar Workspaces no Cargo: Organizando Projetos Grandes em Rust em Produção

O que são Workspaces no Cargo? Um workspace no Cargo é um mecanismo para orga...

Dominando Banco de Dados em Rust com SQLx e PostgreSQL em Projetos Reais
Dominando Banco de Dados em Rust com SQLx e PostgreSQL em Projetos Reais

Introdução ao SQLx e PostgreSQL em Rust SQLx é um driver SQL assincrônico e t...

O que Todo Dev Deve Saber sobre WebAssembly com Rust: Compilando para o Navegador com wasm-pack
O que Todo Dev Deve Saber sobre WebAssembly com Rust: Compilando para o Navegador com wasm-pack

Introdução ao WebAssembly com Rust WebAssembly (WASM) é um formato binário qu...