Rust

Contribuindo com o Ecossistema Rust — Open Source, RFCs e a Comunidade Já leu

14 min de leitura

Contribuindo com o Ecossistema Rust — Open Source, RFCs e a Comunidade
Rust — Artigo #49 Contribuindo com o Ecossistema Rust — Open Source, RFCs e a Comunidade Por Prof. Dr. Marcelo Fontes | Série: Dominando Rust em 1 Ano O Rust qu

Rust — Artigo #49

Contribuindo com o Ecossistema Rust — Open Source, RFCs e a Comunidade

Por Prof. Dr. Marcelo Fontes | Série: Dominando Rust em 1 Ano


O Rust que você aprendeu nesta série não caiu do céu. Cada feature da linguagem passou por um RFC debatido publicamente. Cada crate do ecossistema foi escrita por alguém que resolveu um problema e decidiu compartilhar a solução. O compilador que você usa diariamente tem contribuições de centenas de pessoas ao redor do mundo.

Contribuir com open source em Rust não significa necessariamente escrever código para o compilador. Significa também: reportar bugs com reprodução mínima, melhorar documentação, responder perguntas no fórum, manter uma crate útil, traduzir materiais, ou simplesmente escrever um artigo sobre algo que você aprendeu. Cada forma de contribuição tem valor.

Este artigo cobre o processo técnico e cultural de contribuição — desde abrir seu primeiro issue até propor uma mudança na linguagem.


Anatomia de uma boa contribuição

// Uma boa contribuição resolve um problema real de forma que
// outros possam entender, manter e confiar.

// Contribuições de alto valor (em ordem aproximada de impacto):

// 1. Bug fix com teste de regressão
// 2. Documentação com exemplos executáveis
// 3. Feature com RFC bem fundamentado
// 4. Melhoria de performance com benchmark
// 5. Refatoração que simplifica código existente
// 6. Tradução de documentação

// Contribuições que frequentemente são rejeitadas:
// - "Melhoria" de estilo sem discussão prévia
// - Features sem caso de uso concreto
// - Breaking changes em crates estáveis sem RFC
// - PRs grandes sem issue prévia discutida

Contribuindo para crates existentes

Fluxo de trabalho padrão

# 1. Fork do repositório no GitHub/GitLab

# 2. Clone do seu fork
git clone https://github.com/SEU_USUARIO/nome-da-crate
cd nome-da-crate

# 3. Adiciona o upstream como remote
git remote add upstream https://github.com/AUTOR_ORIGINAL/nome-da-crate

# 4. Cria branch para sua mudança
git checkout -b fix/corrigir-panic-em-input-vazio

# 5. Verifica que os testes passam antes de qualquer mudança
cargo test --all-features
cargo clippy -- -D warnings
cargo fmt --check

# 6. Faz a mudança
# ... edita código ...

# 7. Adiciona teste para a mudança
# ... escreve teste ...

# 8. Verifica novamente
cargo test --all-features
cargo clippy -- -D warnings
cargo fmt

# 9. Commit com mensagem descritiva
git add -p  # revisa cada hunk antes de adicionar
git commit -m "fix: não entra em panic com slice vazio em parsear_tokens()

Antes, passar um slice vazio para parsear_tokens() causava
panic com 'index out of bounds' no acesso a tokens[0].

Adiciona verificação inicial e retorna Err(ErroParser::Vazio)
com mensagem descritiva.

Fixes #142"

# 10. Push e abre PR
git push origin fix/corrigir-panic-em-input-vazio

Escrevendo mensagens de commit excelentes

# Formato: <tipo>(<escopo opcional>): <descrição curta>
#
# <corpo opcional — explica O QUÊ e POR QUÊ, não o COMO>
#
# <rodapé opcional — refs, breaking changes>

# Tipos:
# feat     — nova funcionalidade
# fix      — correção de bug
# docs     — apenas documentação
# style    — formatação, sem mudança de lógica
# refactor — refatoração sem feat nem fix
# perf     — melhoria de performance
# test     — adição/correção de testes
# chore    — manutenção, deps, build

# Exemplos excelentes:

# feat(parser): suporte a comentários de bloco /* ... */
#
# Implementa parsing de comentários de bloco aninhados.
# A especificação da linguagem permite /* /* aninhado */ */.
#
# A implementação usa um contador de profundidade para rastrear
# comentários aninhados, tratando /* como +1 e */ como -1.
#
# Closes #87

# fix(lexer): corrige offset de coluna para caracteres multibyte
#
# O lexer contava bytes em vez de pontos de código Unicode ao
# calcular o número de coluna nos erros. Isso causava erros com
# posições incorretas para código com emojis ou CJK.
#
# Antes: "erro na coluna 8" para código com emoji na coluna 4
# Depois: "erro na coluna 4"
#
# Adiciona testes com strings Unicode em
# tests/lexer_unicode.rs.
#
# Fixes #203

# perf(tokenizer): usa memchr para busca de delimitadores
#
# Substitui loop manual de busca por char por memchr::memchr,
# que usa instruções SIMD quando disponível.
#
# Benchmark (AMD Ryzen 9, 1MB de input):
#   Antes:  12.3ms ± 0.2ms
#   Depois:  3.1ms ± 0.1ms
#   Speedup: 3.97×

Escrevendo um issue excelente

## Bug: `parsear_url()` entra em panic com query string vazia

**Versão da crate:** 2.3.1
**Versão do Rust:** 1.75.0 (rustup show)
**Sistema operacional:** Ubuntu 22.04

### Comportamento esperado

`parsear_url("https://exemplo.com?")` deve retornar `Ok(Url { ... })`
com query string vazia, ou `Err(ErroUrl::QueryInvalida)` — não panic.

### Comportamento atual

thread 'main' panicked at 'index out of bounds: the len is 0 but the index is 0', src/parser.rs:142:8


### Reprodução mínima

use minha_crate::parsear_url;

fn main() { // Isso causa panic: let _url = parsear_url("https://exemplo.com?");

// Isso funciona: let _ok = parsear_url("https://exemplo.com"); }


**Cargo.toml:**

[dependencies] minha-crate = "2.3.1"


### Análise

O panic ocorre em `parser.rs:142` onde o código faz
`partes[0]` sem verificar se `partes` está vazio.
O `split('&')` em uma query string vazia retorna um
iterator com um único elemento vazio `[""]`,
mas o código espera que query strings vazias resultem
em um vetor vazio.

Estou disposto a enviar um PR se a correção desejada
for confirmada.

Criando e mantendo sua própria crate

// Ciclo de vida de uma crate bem mantida:

// FASE 1: Criação
// - Resolve um problema real que você tem
// - Verifica que não existe solução adequada no crates.io
// - Escreve a API como você gostaria de usá-la (API-first)

// FASE 2: Publicação inicial
// - Versão 0.1.0 — API pode mudar sem cerimônia
// - README com exemplo de uso
// - Documentação de todos os itens públicos
// - CI básico (GitHub Actions)
// - Licença MIT OR Apache-2.0 (convenção Rust)

// FASE 3: Estabilização
// - Versão 1.0.0 — commit com API estável
// - Garantia de semver
// - CHANGELOG bem mantido
// - MSRV declarado

// FASE 4: Manutenção de longo prazo
// - Responder issues
// - Aceitar PRs de qualidade
// - Atualizar dependências regularmente
// - Comunicar mudanças breaking com antecedência

// FASE 5 (opcional): Transferência ou arquivamento
// - Se não pode mais manter: encontre um sucessor
// - Se a crate ficou obsoleta: archive e documente alternativas
// - Nunca delete uma crate publicada — quebra projetos de outros

Estrutura de uma crate de qualidade

minha-crate/
├── .github/
│   ├── workflows/
│   │   ├── ci.yml          # testes em todas as plataformas
│   │   ├── release.yml     # publicação automática
│   │   └── dependabot.yml  # atualização automática de deps
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── PULL_REQUEST_TEMPLATE.md
├── src/
│   ├── lib.rs              # ponto de entrada + doc do crate
│   ├── error.rs            # tipos de erro centralizados
│   └── ...
├── tests/
│   └── integration.rs      # testes de API pública
├── examples/
│   ├── basico.rs
│   └── avancado.rs
├── benches/
│   └── principal.rs
├── fuzz/
│   └── fuzz_targets/
├── Cargo.toml
├── README.md               # exemplos executáveis
├── CHANGELOG.md            # histórico de versões
├── CONTRIBUTING.md         # guia para contribuidores
├── LICENSE-MIT
├── LICENSE-APACHE
└── .rustfmt.toml           # configuração de formatação

CONTRIBUTING.md bem escrito

# Guia de Contribuição

Obrigado por considerar contribuir! Este documento descreve
como participar do projeto.

## Antes de começar

Para mudanças grandes, abra uma issue primeiro para discutir
a abordagem. Isso evita trabalho desperdiçado se a direção
não estiver alinhada com os objetivos do projeto.

## Configuração do ambiente

git clone https://github.com/usuario/minha-crate cd minha-crate rustup show # instala a toolchain correta via rust-toolchain.toml cargo test # deve passar sem erros


## Padrões de código

- `cargo fmt` antes de cada commit (ou configure seu editor)
- `cargo clippy -- -D warnings` deve passar sem erros
- Documentação em português para itens públicos
- Testes para cada bug corrigido e feature adicionada

## Processo de PR

1. Fork → branch → commits → PR
2. PR deve referenciar a issue relacionada
3. Todos os checks de CI devem estar verdes
4. Aguarde revisão (geralmente em 1-2 semanas)

## O que acontece após o merge?

Seu nome aparece nos releases notes. A próxima versão
pode incluir sua mudança. Obrigado!

## Código de Conduta

Este projeto segue o [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct).

O processo de RFC da linguagem Rust

# Como features chegam ao Rust

## 1. Problema identificado na comunidade
   Alguém tem um pain point real — "não consigo expressar X sem
   fazer Y que é verbose/inseguro/lento"

## 2. Discussão informal
   - Internals forum (internals.rust-lang.org)
   - Zulip (#t-lang, #t-compiler, etc.)
   - Issues no repositório rust-lang/rust

## 3. RFC escrito e publicado
   - Fork de rust-lang/rfcs
   - Arquivo em text/0000-nome-da-feature.md
   - PR aberto para rust-lang/rfcs

## 4. Período de comentários (FCP)
   - Qualquer pessoa pode comentar
   - Times relevantes (t-lang, t-libs, t-compiler) reviram
   - Modificações baseadas no feedback

## 5. Decisão do time
   - FCP (Final Comment Period): 10 dias para objeções finais
   - Merge: RFC aceito e numerado
   - Close: RFC rejeitado com explicação

## 6. Implementação
   - Feature gate: `#![feature(nome_da_feature)]`
   - Apenas em nightly inicialmente
   - Tracking issue aberto

## 7. Estabilização
   - Implementação completa e testada
   - Documentação escrita
   - FCP de estabilização
   - Disponível em stable

# Exemplos de RFCs famosos:
# RFC 221  — struct update syntax ({ ..outro })
# RFC 243  — trait objects (dyn Trait)
# RFC 505  — API de I/O estável
# RFC 1229 — redefinição de overflow
# RFC 2094 — NLL (Non-Lexical Lifetimes)
# RFC 2632 — async/await
# RFC 3498 — async closures (Rust 2024)

Escrevendo um RFC

# RFC: Interpolação de strings com `f"{}"`

- Feature Name: `string_interpolation`
- Start Date: 2024-03-15
- RFC PR: rust-lang/rfcs#XXXX
- Rust Issue: rust-lang/rust#XXXXX

## Resumo

Adiciona sintaxe `f"texto {expressão} mais texto"` para
interpolação de strings formatadas, similar ao Python f-strings
e JavaScript template literals.

## Motivação

Atualmente, formatar strings requer `format!("{}", valor)` ou
`format!("{nome}", nome = valor)`. Para expressões complexas,
isso se torna verbose:

// Atual — repetitivo let s = format!( "Usuário {nome} tem {idade} anos e mora em {cidade}", nome = usuario.nome, idade = usuario.calcular_idade(), cidade = usuario.endereco.cidade, );

// Proposto — mais direto let s = f"Usuário {usuario.nome} tem {usuario.calcular_idade()} anos e mora em {usuario.endereco.cidade}";


## Guia de uso

[Explica como a feature funcionaria na prática]

## Referência

[Especificação técnica detalhada]

## Inconvenientes

- Complexidade adicional no parser
- Possível conflito com strings em macros
- [outros tradeoffs]

## Alternativas consideradas

- `format!` com sintaxe de captura implícita (RFC 2795,
  estabilizado como `format_args_capture`)
- Macro `s!` de crate de terceiros

## Perguntas não resolvidas

- Como tratar expressões com chaves literais `{{}}`?
- Permitir especificadores de formato `{valor:.2}`?

Comunidade Rust — onde participar

// Canais oficiais:

// Forum de usuários: users.rust-lang.org
// → Perguntas, discussões sobre uso, showcase de projetos

// Forum interno: internals.rust-lang.org
// → Discussão sobre a linguagem, futuras features, design

// Zulip: rust-lang.zulipchat.com
// → Chat em tempo real, times organizados por tópico
// → #beginners — ótimo para perguntas
// → #t-lang — discussões da linguagem
// → #t-libs — discussões das bibliotecas padrão
// → #general — conversa geral

// Reddit: r/rust
// → Notícias, projetos, "This Week in Rust"

// Discord: discord.gg/rust-lang-community
// → Comunidade não-oficial, muito ativa

// GitHub: github.com/rust-lang
// → Issues, PRs, RFCs

// "This Week in Rust": this-week-in-rust.org
// → Newsletter semanal curada pela comunidade
// → Você pode submeter itens para a newsletter!

Contribuindo com a documentação

# O livro oficial (The Rust Programming Language)
git clone https://github.com/rust-lang/book
cd book

# Instala mdBook
cargo install mdbook

# Visualiza localmente
mdbook serve --open

# Rust Reference — especificação informal
git clone https://github.com/rust-lang/reference

# Rustonomicon — unsafe Rust
git clone https://github.com/rust-lang/nomicon

# Rust By Example
git clone https://github.com/rust-lang/rust-by-example

# Tradução do livro para português
git clone https://github.com/rust-br/rust-book-pt-br

Tipos de contribuição de documentação

// 1. Corrigir imprecisões técnicas
// 2. Adicionar exemplos onde há apenas descrição abstrata
// 3. Melhorar fluxo e clareza do texto
// 4. Atualizar para versões mais recentes do Rust
// 5. Adicionar seções sobre casos de uso não cobertos

// Exemplo: melhorando documentação de uma função

// ANTES — pouco informativo:
/// Converte para string.
pub fn para_string(&self) -> String {
    format!("{}", self.valor)
}

// DEPOIS — completo e útil:
/// Converte o valor para sua representação textual.
///
/// O formato produzido é adequado para exibição ao usuário
/// mas não necessariamente para serialização — use
/// [`Serialize`] para serialização estruturada.
///
/// # Exemplos
///
/// ```rust
/// use minha_crate::Temperatura;
///
/// let t = Temperatura::celsius(100.0);
/// assert_eq!(t.para_string(), "100°C");
///
/// let t = Temperatura::fahrenheit(212.0);
/// assert_eq!(t.para_string(), "212°F");
/// ```
///
/// # Nota sobre precisão
///
/// Valores com muitas casas decimais são arredondados para
/// duas casas na representação textual:
///
/// ```rust
/// # use minha_crate::Temperatura;
/// let t = Temperatura::celsius(36.666666);
/// assert_eq!(t.para_string(), "36.67°C");
/// ```
pub fn para_string(&self) -> String {
    match self.unidade {
        Unidade::Celsius    => format!("{:.2}°C", self.valor),
        Unidade::Fahrenheit => format!("{:.2}°F", self.valor),
        Unidade::Kelvin     => format!("{:.2}K",  self.valor),
    }
}

Mantendo saúde em projetos open source

// Coisas que funcionam a longo prazo:

// 1. DEFINA ESCOPO CLARAMENTE
// Um README com "O que esta crate FAZ" e "O que NÃO FAZ"
// reduz issues fora de escopo e expectativas incorretas.

// 2. USE LABELS ESTRATEGICAMENTE
// good-first-issue  → atrai novos contribuidores
// help-wanted       → você aceita PRs para isso
// wontfix           → fora de escopo, evita reaberturas
// needs-info        → aguardando resposta do autor da issue

// 3. TEMPLATES DE ISSUE
// Reduzem drasticamente o número de issues incompletas.
// Um template de bug que pede: versão, SO, reprodução mínima
// filtra 80% dos "não funciona" sem contexto.

// 4. AUTOMATIZE O QUE PUDER
// - cargo-release para publicação automática
// - dependabot para atualização de deps
// - bors/merge-queue para integração contínua
// - changelog automático via conventional commits

// 5. COMUNIQUE DISPONIBILIDADE
// Se vai entrar em hiato, diga no README.
// "Maintainer em modo passivo — PRs aceitos, respostas lentas"
// é muito melhor que silêncio.

// 6. ENCONTRE CO-MANTENEDORES CEDO
// Não espere estar sobrecarregado.
// Contribuidores frequentes são candidatos naturais.

Um projeto completo: contribuindo para serde

Vamos simular o processo completo de contribuir com uma melhoria real:

# Cenário: você encontrou que serde não tem suporte a
# deserialização de durations no formato "1h30m45s"
# Você quer adicionar essa funcionalidade

# 1. Verifica que não existe (issues, crates.io)
cargo search "serde duration"
# humantime-serde existe! — pesquise antes de implementar

# Se não existisse:

# 2. Abre issue para discussão
# Título: "feat: deserializar Duration de strings humanas (1h30m)"

# 3. Fork e clone
git clone https://github.com/SEU_USUARIO/serde
git remote add upstream https://github.com/serde-rs/serde

# 4. Entende a estrutura
find . -name "*.rs" | xargs grep -l "Duration" | head -10

# 5. Escreve o código
cat > serde/src/de/impls.rs << 'EOF'
// Adiciona impl de Deserialize para Duration com formato string
// ... implementação ...
EOF

# 6. Escreve testes
cat > test_suite/tests/test_de.rs << 'EOF'
#[test]
fn test_duration_string() {
    let d: Duration = serde_json::from_str(r#""1h30m45s""#).unwrap();
    assert_eq!(d, Duration::from_secs(5445));
}
EOF

# 7. Benchmark comparativo
cargo bench --bench duration_de

# 8. Documentação
# Atualiza docs com o novo formato suportado

# 9. PR com descrição detalhada

Template de PR bem escrito:

## Descrição

Adiciona suporte a deserialização de `Duration` a partir de
strings no formato humano (`"1h30m45s"`, `"2min"`, `"500ms"`).

## Motivação

Configurações frequentemente expressam durations como strings
legíveis (`timeout: "30s"`). Sem esse suporte, usuários precisam
implementar deserialização customizada ou usar crates auxiliares.

## Mudanças

- `serde/src/de/impls.rs`: novo impl de `Deserialize` para
  `Duration` que tenta string antes de tentar número
- `test_suite/tests/test_de.rs`: 12 novos testes cobrindo
  formatos válidos e inválidos
- `serde/src/de/impls.rs`: documentação atualizada com exemplos

## Compatibilidade

**Breaking change**: Não — apenas adiciona novo comportamento.

Valores numéricos continuam funcionando como antes (segundos).
Strings no novo formato são um caminho adicional.

## Testes

cargo test --all-features running 847 tests test result: ok. 847 passed; 0 failed


## Checklist

- [x] Testes adicionados
- [x] Documentação atualizada
- [x] CHANGELOG atualizado
- [x] `cargo clippy -- -D warnings` passa
- [x] `cargo fmt --check` passa
- [x] Testado no MSRV (Rust 1.56)

Closes #1847

Reconhecimento e sustentabilidade

// Open source sustentável não é só código:

// RECONHECIMENTO DE CONTRIBUIDORES
// all-contributors.rc ou CONTRIBUTORS.md
// Reconhece: código, docs, issues, testes, design, etc.

// FINANCIAMENTO
// GitHub Sponsors — direto para mantenedores
// Open Collective — para projetos/organizações
// FOSS Foundations — Apache, Linux Foundation, etc.

// TRANSFERÊNCIA DE PROJETOS
// Se não pode mais manter, o processo correto é:
// 1. Anuncia no README e issues abertas
// 2. Oferece transferência no GitHub
// 3. Publica nova versão com aviso de depreciação
//    apontando para o fork ativo
// 4. NUNCA deleta o repositório (quebra dependências)

// IMPORTANTE: Rust tem uma política de "squatting" no crates.io
// Crates abandonadas podem ser reivindicadas após processo formal
// Veja: https://crates.io/policies

Fontes e leituras recomendadas

  • "The Rust RFC Process" — como propor mudanças — https://github.com/rust-lang/rfcs
  • "Contributing to Rust" — guia oficial — https://rustc-dev-guide.rust-lang.org/contributing.html
  • "This Week in Rust" — newsletter semanal — https://this-week-in-rust.org
  • "Rust Forge" — recursos para contribuidores — https://forge.rust-lang.org
  • Zulip da comunidade Rust — https://rust-lang.zulipchat.com
  • "Working in Public" — Nadia Eghbal — o melhor livro sobre manutenção de open source
  • "Producing Open Source Software" — Karl Fogel — gratuito online — https://producingoss.com
  • crates.io policies — https://crates.io/policies
  • Rust Code of Conduct — https://www.rust-lang.org/policies/code-of-conduct

Artigo #49 de 52 | Série: Dominando Rust em 1 Ano Próximo → Artigo #50: O Futuro do Rust — Async traits, GATs, especialização e o roadmap da linguagem


Comentários

Mais em Rust

Generics — Código que Funciona para Qualquer Tipo
Generics — Código que Funciona para Qualquer Tipo

&nbsp; No artigo anterior aprendemos que traits definem&nbsp;o que um tipo p...

Rust e Machine Learning — Inferência, ONNX e Aceleração de Modelos
Rust e Machine Learning — Inferência, ONNX e Aceleração de Modelos

Rust — Artigo #46 Rust e Machine Learning — Inferência, ONNX e Aceleração de...

Smart Pointers — Box, Rc e RefCell
Smart Pointers — Box, Rc e RefCell

&nbsp; At&eacute; agora trabalhamos com dados na stack e refer&ecirc;ncias s...