Evitando Clones
clone() é explícito e frequentemente caro. Projete APIs em torno de empréstimos, Cow e propriedade movida para que as cópias ocorram apenas em limites intencionais.
Receita
use std::borrow::Cow;
fn greet(name: Cow<'_, str>) {
println!("Hello, {name}");
}
greet(Cow::Borrowed("Ada"));
greet(Cow::Owned("Bob".into()));Quando usar isso: Clippy redundant_clone dispara em caminhos quentes ou revisa pontos clone() em loops.
Exemplo de Trabalho
struct Request<'a> {
path: &'a str,
body: Cow<'a, [u8]>,
}
fn handle(req: Request<'_>) -> Status {
if req.path == "/health" {
return Status::Ok;
}
process_body(req.body.as_ref())
}O que isso demonstra:
- Empréstimos
&str/&[u8]quando o chamador possui os dados Cowaceita emprestado ou possuído sem forçar clone antecipadamente- Move
Stringuma vez para a struct em vez de clonar por uso Arc<str>para strings imutáveis compartilhadas entre threads
Mergulho Profundo
Estratégias
| Padrão | Uso |
|---|---|
Parâmetros &T / &str | Caminhos quentes somente leitura |
Cow<'a, T> | Às vezes precisa de propriedade |
| Mover por valor | Consumidor único |
Arc<T> | Dados compartilhados com muita leitura |
Rc<T> | Compartilhamento em thread única |
Exemplo Axum
async fn handler(Json(body): Json<CreateOrder>) -> impl IntoResponse {
// Json<T> possui o corpo uma vez; passe referências para a camada de domínio
service.create(&body).await
}Armadilhas
- Clonar para satisfazer o verificador de empréstimo - curativo. Correção: reestruture lifetimes, use dados possuídos em tarefas assíncronas ou
Arc. - Clonar
Arcpensando que é clone de dados barato -Arc::cloneé barato; o clone interno deStringnão é. Correção: distinga clone de ponteiro vs clone de dados. - Cow em todos os lugares - complexidade da API. Correção: use onde chamadores emprestados e possuídos existem.
- Clone implícito em
to_owned()em loop - mesmo custo queclone(). Correção: lote ou empreste. - serde sempre possuído - use
#[serde(borrow)]em campos&strao desserializar de um buffer&[u8].
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
Arc<str> | Strings imutáveis compartilhadas | Mutação única necessária |
Pin<Box<T>> | Auto-referencial | Valores movidos simples |
&'static str | Literais em tempo de compilação | Entrada dinâmica do usuário |
FAQs
Quando clone é OK?
Em limites de IO, ao gerar tarefas que precisam de 'static, ou tipos Copy baratos.
Desempenho do Cow?
Pequeno discriminador de enum; custo de ramificação negligenciável vs clone de heap salvo.
Async e dados emprestados?
Tarefas geradas precisam de 'static; use String possuída ou Arc no limite de geração uma vez.
Como encontrar clones redundantes?
cargo clippy -- -W clippy::redundant_clone e perfiladores.
Clone vs copy?
Tipos Copy (inteiros, refs) são bit a bit; Clone pode alocar no heap.
Coleção de iterador clona?
collect::<Vec<_>>() clona se iterar referências; use into_iter() ao consumir.
sqlx Row get clona?
Use try_get para valores tipados; busque &str apenas com padrões de lifetime compatíveis ou copie uma vez.
Polars clona colunas?
Plano lazy compartilhado; clone() em DataFrame é cópia de dados - minimize coleções.
Padrão Builder clona?
O builder move campos; evite #[derive(Clone)] em configurações grandes.
Regra de ouro da revisão?
Um clone() por solicitação é OK; clone por linha em um loop de um milhão de linhas precisa de justificativa.
Relacionado
- Entendendo Alocações - custo de heap
- Cow, Clone on Write - detalhe do ponteiro inteligente
- Padrões Anti-Padrões Comuns - excesso de clonagem
- 30 Regras de Desempenho
Versões de Stack: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, e Polars 0.46+.