«How do I convert between String, &str, Vec<u8> and &[u8]?» — sua lista de verificação para otimização em Rust
\n\nSe você já escreveu código Rust que trabalha com texto ou bytes, certamente já se deparou com a pergunta: «Como converto String para &str? E Vec<u8> para &[u8]? E no sentido inverso?». Esta é uma das perguntas mais populares no Stack Overflow (dezenas de milhares de visualizações), e não é à toa. Conversões aparentemente simples escondem armadilhas, especialmente quando se trata de desempenho.
Neste artigo, não vamos apenas listar as formas de «como fazer». Vamos analisar por que alguns métodos são mais rápidos que outros e quando usar cada um deles. Você aprenderá como evitar cópias desnecessárias, alocações extras e como escrever código verdadeiramente eficiente em Rust.
\n\nPor que saber disso? Contexto e desempenho
\nRust é uma linguagem que lhe dá controle sobre a memória. Mas com controle vem responsabilidade. A conversão entre strings e bytes é uma das operações mais comuns. Por exemplo, ao trabalhar com protocolos de rede, serialização de dados (JSON, Protobuf) ou simplesmente leitura de arquivos. A escolha errada de conversão pode levar à cópia de dados que poderiam ser usados sem cópia. No mundo da programação de sistemas, isso é crítico.
\n\nVamos analisar os quatro tipos principais e aprender a transitar entre eles sem perdas.
\n\nString e &str: dois lados da mesma moeda
\nString é uma string UTF-8 proprietária e mutável. &str é uma fatia (slice) de string emprestada e imutável. A regra principal: String é o proprietário dos dados, &str é uma referência aos dados.
Como obter &str de String (e vice-versa)
\nEste é o caso mais simples e seguro.
\n\nlet my_string = String::from("Olá, mundo!");\n// 1. Obtendo &str de String (apenas emprestando)\nlet my_str: &str = &my_string;\n// Ou usando as_str()\nlet my_str2: &str = my_string.as_str();\n\n// 2. Obtendo String de &str (criando uma nova cópia)\nlet new_string: String = my_str.to_string();\n// Ou\nlet new_string2: String = String::from(my_str);\n\nImportante: A conversão de &str para String sempre cria nova memória alocada (alocação). Isso é caro. A conversão de String para &str é apenas pegar uma referência (gratuita).
Como obter Vec<u8> de String (e vice-versa)
\nAqui começa a parte interessante, porque String internamente é apenas um Vec<u8> com garantia de UTF-8 válido.
let my_string = String::from("Rust");\n\n// 3. String -> Vec<u8>\n// Opção A (cópia): segura, mas cara\nlet bytes_vec_copy: Vec<u8> = my_string.as_bytes().to_vec();\n\n// Opção B (sem cópia, consumindo String):\nlet bytes_vec_owned: Vec<u8> = my_string.into_bytes(); // my_string não existe mais!\n\n// 4. Vec<u8> -> String (verificação de UTF-8 válido)\nlet new_string: String = String::from_utf8(bytes_vec_owned).expect("UTF-8 inválido");\n// Se você tem certeza da validade:\nlet new_string_unchecked: String = unsafe { String::from_utf8_unchecked(bytes_vec_copy) };\n\nPonto-chave: into_bytes() é uma conversão de custo zero. Ela pega o buffer interno Vec<u8> e o entrega a você, sem copiar um único byte. as_bytes().to_vec() é uma cópia explícita.
O método from_utf8_unchecked é unsafe. Use-o apenas se você tiver 100% de certeza de que os bytes são UTF-8 válido. Caso contrário, o programa pode entrar em pânico ou, pior, apresentar UB (comportamento indefinido).