Introdução: A armadilha em que todos os iniciantes em Rust caem
\n\nVocê está aprendendo Rust. Você já entendeu o conceito de posse e empréstimo. Você está escrevendo sua primeira função séria e pensa: "Ah, para não mover a string, vou passar uma referência!". E você escreve:
\n\nfn print_string(s: &String) {\n println!("{}", s);\n}\n\nO compilador está feliz. O código funciona. Você está feliz. Mas o problema é: um desenvolvedor Rust experiente, ao ver seu código, vai franzir a testa. Por quê? Afinal, tudo funciona!
\n\nEsta pergunta é uma das mais populares no StackOverflow. Milhares de desenvolvedores tropeçam neste detalhe aparentemente insignificante. Hoje vamos entender por que &String, &Vec e &Box na assinatura de uma função são uma bandeira vermelha e como escrever corretamente.
Problema nº1: Restrição implícita de tipos (Type Coercion)
\n\nRust possui um sistema poderoso de coerção automática de tipos, mas apenas em uma direção. Isso é chamado de Deref Coercion (coerção por desreferenciamento).
\n\nA ideia é simples: se você tem uma referência a String, Rust automaticamente pode transformá-la em uma referência a &str. O mesmo acontece com &Vec<T> → &[T] e &Box<T> → &T.
Veja a diferença:
\n\n// Ruim: aceita apenas &String\nfn bad(s: &String) {\n println!("{}", s);\n}\n\n// Bom: aceita &str\nfn good(s: &str) {\n println!("{}", s);\n}\n\nfn main() {\n let owned = String::from("Olá, mundo!");\n let slice = "Olá, slice!";\n \n bad(&owned); // Funciona\n // bad(slice); // ERRO! slice é &str, não &String\n \n good(&owned); // Funciona (coerção automática)\n good(slice); // Funciona (já é &str)\n}\n\nViu? A função good aceita qualquer string: tanto a proprietária String, quanto o literal de string &str, e até mesmo parte de uma string. A função bad aceita apenas String. Isso é uma restrição desnecessária.
Exemplo prático: busca de substring
\n\n// Busca limitada\nfn find_in_string(haystack: &String, needle: &str) -> bool {\n haystack.contains(needle)\n}\n\n// Busca universal\nfn find_in_str(haystack: &str, needle: &str) -> bool {\n haystack.contains(needle)\n}\n\nfn main() {\n let my_string = String::from("Rust é incrível");\n let my_str = "Rust é incrível";\n \n // Funciona apenas com String\n find_in_string(&my_string, "Rust");\n // find_in_str(my_str, "Rust"); // Erro de compilação!\n \n // Funciona com ambos\n find_in_str(&my_string, "Rust");\n find_in_str(my_str, "Rust");\n}\n\nProblema nº2: Desempenho e indireções desnecessárias
\n\nVamos olhar "por baixo do capô". O que é String na memória? É uma estrutura com três campos:
- \n
- Ponteiro para os dados no heap \n
- Comprimento da string \n
- Capacidade (capacity) \n
Quando você passa &String, você passa uma referência para esta estrutura. Para acessar os dados, o compilador precisa:
- \n
- Desreferenciar
&String→ obter a estruturaString\n - Da estrutura, pegar o ponteiro para os dados no heap \n
- Desreferenciar este ponteiro → obter os bytes da string \n
Quando você passa &str, você passa um fat pointer (ponteiro gordo), que consiste em apenas dois campos:
- \n
- Ponteiro para os dados \n
- Comprimento \n
O compilador precisa dar apenas um passo — desreferenciar o ponteiro. Menos indireção significa código mais rápido. A diferença é microscópica, mas em loops críticos ou ao passar para