Rust: Um guia completo sobre strings no Rust

Última atualização: 2026-08-26

O Rust não tem apenas um, mas dois tipos de string — porque a programação de sistemas exige um controle preciso sobre a memória.

O manuseio de strings no Rust é motivo de confusão para muitos iniciantes: por que existem dois tipos — String e &str? Em que eles diferem do char* em C? Esta lição vai ajudar você a entender tudo isso.


1. O que você vai aprender


2. A história de uma bibliotecária

(1) Problema: Os dados JSON são muito longos para caberem

Maria é uma bibliotecária que está desenvolvendo um pequeno sistema de busca de livros para gerenciar 5.000 livros.

“Seria ótimo se houvesse strings que gerenciassem a memória automaticamente...” Mais tarde, ela encontrou a resposta no Rust.

(2) A solução para strings no Rust

RUST
fn main() {
    // &str: String Literals, Present in the binary at compile time
    let title_book1: &str = "One Hundred Years of Solitude";

    // String: Allocated on the heap, Variable length, Automatic Memory Management
    let mut title_book2 = String::from("How to Make a Complete Map of an Infinite Universe");
    title_book2.push_str(" (Second Edition)");  // Additional Content

    println!("Book 1: {} (Length: {} Bytes)", title_book1, title_book1.len());
    println!("Book 2: {} (Length: {} Bytes)", title_book2, title_book2.len());
    // String Automatically released when leaving the scope, No manual free required
}

O String do Rust é semelhante às strings do Java — ele gerencia a memória automaticamente; o &str é semelhante ao const char* do C — referências somente para leitura. Juntos, eles alcançam um equilíbrio entre desempenho e flexibilidade.


3. A diferença entre &str e String

(1) Modelo de memória

100%
graph LR
    subgraph "&str (String Literals)"
        S1_ptr[ptr ──→ ...rust...]
        S1_len[len: 4]
    end
    subgraph "String (Heap Allocation)"
        S2_ptr[ptr ──→ Rust is...]
        S2_len[len: 10]
        S2_cap[cap: 16]
    end
    subgraph "Storage Location"
        STACK[Stack: &str<br>Read-Only Borrow]
        HEAP[Heap: String<br>Ownership]
    end
    S1_ptr -.->|Determined at compile time| STACK
    S2_ptr -.->|Runtime Allocation| HEAP
Características &str String
Proprietário Acesso somente para leitura Propriedade total
Mutabilidade Imutável Pode ser acrescentado/modificado
Local de armazenamento Segmento de dados somente leitura (em tempo de compilação) Heap (alocado em tempo de execução)
Método de criação Literal "hello" String::from() ou .to_string()
Casos de uso Parâmetros de string somente leitura É necessário modificar ou assumir a propriedade da string

(2) Referência rápida aos métodos comuns de strings

Método Tipo de retorno Descrição Exemplo
push_str(&str) () Trecho adicional da sequência de caracteres s.push_str("abc")
push(char) () Adicionar um único caractere s.push('!')
len() usize Comprimento do byte de retorno s.len()
is_empty() bool Está vazio? s.is_empty()
trim() &str Remover espaços à esquerda e à direita s.trim()
contains(&str) bool Se a subsequência está incluída s.contains("rust")
replace(&str, &str) String Substituir todas as ocorrências s.replace("a", "b")
Dividir por delimitador split(char) Split s.split(',')
Converter para letras minúsculas
Letras maiúsculas to_uppercase() String Alternar para maiúsculas
chars() Chars Iterar por caractere s.chars()
bytes() Bytes Iterar por bytes s.bytes()
Limpar string
capacity() usize Voltar para Capacidade do buffer s.capacity()

(3) Método de criação

RUST
// &str Create
let s1: &str = "hello, world";       // String Literals
let s2: &str = r"raw string\n";      // Original string(Do not escape)

// String Create
let s3 = String::from("hello");
let s4 = "world".to_string();
let s5 = format!("{}-{}", s3, s4);   // Format and Create
let s6: String = "hi".into();         // Through Type Conversion

(4) Referência rápida aos métodos de conversão de strings

Tipo de origem Tipo de destino Método de conversão Sem sobrecarga
Alocar memória do heap
Alocar memória do heap
Alocar memória do heap
String &str &s ou s.as_str() Custo zero (empréstimo)
String &str &s[..] Custo zero (fatia)
&str &[u8] "hello".as_bytes() Custo zero (empréstimo)
String Vec<u8> s.into_bytes() Sem custos adicionais (transferência de propriedade)
Vec<u8> String String::from_utf8(v) Sem sobrecarga (transferência de propriedade; verificação UTF-8 necessária)

4. Operações comuns com strings

▶ Exemplo 1: Criação e modificação de strings (Dificuldade ⭐)

RUST
// ============================================
// String Creation, Append and Concatenation
// ============================================

fn main() {
    // Create
    let mut s = String::from("Hello");

    // Add
    s.push_str(", ");       // Append a string slice
    s.push('R');            // Add a single character
    s.push_str("ust!");
    println!("After the addition: {}", s);

    // Concatenation (+ operator consumes the left-hand side String)
    let s1 = String::from("Hello, ");
    let s2 = String::from("World!");
    let s3 = s1 + &s2;      // s1 Moved, It can't be used anymore.
    println!("After concatenation: {}", s3);

    // format! macro (Does not consume any variables)
    let a = String::from("tic");
    let b = String::from("tac");
    let c = String::from("toe");
    let result = format!("{}-{}-{}", a, b, c);
    println!("format Results: {}", result);
}

Resultado:

TEXT 📖 Somente leitura
After the addition: Hello, Rust!
After concatenation: Hello, World!
format Result: tic-tac-toe

O operador + consome (move) o String à sua esquerda; portanto, o s1 não pode mais ser usado após a concatenação. O format! não consome nenhuma variável; a propriedade de todas as variáveis é mantida.


▶ Exemplo 2: Segmentação e indexação de strings (Dificuldade ⭐⭐)

RUST
// ============================================
// String Slicing -- Note UTF-8 Boundary!
// ============================================

fn main() {
    let s = String::from("Rust🦀Code");

    // Get the length of a string (Number of bytes, Not the number of characters)
    println!("Byte Length: {}", s.len());         // 11 (4 Byte ASCII + 4 Byte UTF-8 emoji + 4 Byte ASCII)

    // String Slicing (Index by Byte, Must be at a character boundary)
    let slice1 = &s[0..4];                     // "Rust" (first 4 bytes fall exactly on a character boundary)
    println!("First 4 Byte Slice: {}", slice1);

    // &s[0..5] will panic! (Because bytes 4-5 are in the middle of the emoji)

    // Iterate by character count
    for c in s.chars() {
        print!("{} ", c);
    }
    println!();

    // Iterate by byte
    for b in s.bytes() {
        print!("{:02x} ", b);
    }
    println!();
}

Resultado:

TEXT 📖 Somente leitura
Byte Length: 11
First 4 Byte Slice: Rust
R u s t 🦀 C o d e
52 75 73 74 f0 9f a6 80 43 6f 64 65

As strings do Rust não suportam acesso direto por índice (como s[0]), pois os caracteres UTF-8 têm comprimento variável. É necessário percorrer os caracteres usando .chars() ou utilizar fatias de bytes. As fatias devem estar alinhadas com os limites dos caracteres; caso contrário, ocorrerá uma falha em tempo de execução.


▶ Exemplo 3: Métodos comuns para strings (Dificuldade ⭐⭐)

RUST
// ============================================
// Common String Queries, Replace, Cutting Methods
// ============================================

fn main() {
    let s = "  Hello, Rust World!  ";

    // Remove leading and trailing spaces
    println!("trim: '{}'", s.trim());

    // Includes a check
    println!("contains 'Rust': {}", s.contains("Rust"));

    // Replace
    let replaced = s.replace("Rust", "Go");
    println!("replace: '{}'", replaced);

    // Split
    let parts: Vec<&str> = "apple,banana,cherry".split(',').collect();
    println!("split: {:?}", parts);

    // Case Conversion
    let lower = "HELLO".to_lowercase();
    let upper = "world".to_uppercase();
    println!("lower: {}, upper: {}", lower, upper);
}

Resultado:

TEXT 📖 Somente leitura
trim: 'Hello, Rust World!'
contains 'Rust': true
replace: '  Hello, Go World!  '
split: ["apple", "banana", "cherry"]
lower: hello, upper: WORLD

trim() e split() retornam &str sem criar uma nova string. replace() retorna um String totalmente novo. Leve em consideração a sobrecarga de desempenho ao fazer modificações.


▶ Exemplo 4: Iteração por strings UTF-8 e verificações de limites (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// UTF-8 Bytes in a string, Character and Grapheme Clusters
// ============================================

fn main() {
    let s = "Rust🦀Code";

    println!("String: {}", s);
    println!("Byte Length: {}", s.len());
    println!("Number of characters: {}", s.chars().count());

    println!("\n--- Character-by-character traversal ---");
    for (i, c) in s.chars().enumerate() {
        println!("Character {}: '{}' (Unicode: U+{:04X})", i, c, c as u32);
    }

    println!("\n--- Traverse byte by byte ---");
    for (i, b) in s.bytes().enumerate() {
        print!("{:02x} ", b);
        if (i + 1) % 8 == 0 { println!(); }
    }
    println!();

    println!("\n--- Safe Slices ---");
    let ascii_part = &s[0..4];
    println!("ASCII Part &s[0..4]: '{}'", ascii_part);

    let emoji_start = 4;
    let emoji_end = emoji_start + 4;
    let emoji_part = &s[emoji_start..emoji_end];
    println!("Emoji Part &s[{}..{}]: '{}'", emoji_start, emoji_end, emoji_part);

    let code_start = emoji_end;
    let code_end = code_start + 4;
    let code_part = &s[code_start..code_end];
    println!("Code Part &s[{}..{}]: '{}'", code_start, code_end, code_part);

    let combined = format!("{}{}{}", ascii_part, emoji_part, code_part);
    println!("Concatenated Reconstruction: {}", combined);
}

Resultado:

TEXT 📖 Somente leitura
String: Rust🦀Code
Byte Length: 12
Number of characters: 8

--- Character-by-character traversal ---
Character 0: 'R' (Unicode: U+0052)
Character 1: 'u' (Unicode: U+0075)
Character 2: 's' (Unicode: U+0073)
Character 3: 't' (Unicode: U+0074)
Character 4: '🦀' (Unicode: U+1F980)
Character 5: 'C' (Unicode: U+0043)
Character 6: 'o' (Unicode: U+006F)
Character 7: 'd' (Unicode: U+0064)
Character 8: 'e' (Unicode: U+0065)

--- Traverse byte by byte ---
52 75 73 74 f0 9f a6 80 43 6f 64 65

--- Safe Slices ---
ASCII Part &s[0..4]: 'Rust'
Emoji Part &s[4..8]: '🦀'
Code Part &s[8..12]: 'Code'
Concatenated Reconstruction: Rust🦀Code

No UTF-8, os caracteres ASCII ocupam 1 byte e os emojis, 4 bytes. A divisão deve ocorrer nos limites entre caracteres; caso contrário, ocorrerá um erro grave em tempo de execução. Use .chars().enumerate() para processar os caracteres um por um com segurança.


▶ Exemplo 5: Análise e formatação prática de strings (Dificuldade: ⭐⭐)

RUST
// ============================================
// String Parsing, Combined Use of Formatting and Linking
// ============================================

fn main() {
    let raw_data = "name=Alice;age=30;city=Beijing";

    println!("Raw Data: {}", raw_data);
    println!("\n--- Parsing Key-Value Pairs ---");

    let pairs: Vec<&str> = raw_data.split(';').collect();
    for pair in pairs {
        let parts: Vec<&str> = pair.split('=').collect();
        if parts.len() == 2 {
            let key = parts[0].trim();
            let value = parts[1].trim();
            println!("{} => {}", key, value);
        }
    }

    println!("\n--- Construct a New String ---");
    let name = "Alice";
    let age = 30;
    let city = "Beijing";
    let summary = format!("{}, {} years old, From {}", name, age, city);
    println!("Abstract: {}", summary);

    let mut report = String::from("Personnel Report\n");
    report.push_str(&format!("Name: {}\n", name));
    report.push_str(&format!("Age: {}\n", age));
    report.push_str(&format!("City: {}\n", city));
    report.push_str("--- End ---");
    println!("\n{}", report);

    let csv_line = ["Alice", "30", "Beijing"].join(",");
    println!("\nCSV Format: {}", csv_line);
}

Resultado:

TEXT 📖 Somente leitura
Raw Data: name=Alice;age=30;city=Beijing

--- Parsing Key-Value Pairs ---
name => Alice
age => 30
city => Beijing

--- Construct a New String ---
Abstract: Alice, 30 years old, From Beijing

Personnel Report
Name: Alice
Age: 30
City: Beijing
--- End ---

CSV Format: Alice,30,Beijing

split() + collect() são os padrões básicos para a análise de texto estruturado; format! é adequado para concatenação única; push_str() é adequado para a construção gradual de textos longos; join() é adequado para conectar matrizes com delimitadores.


❓ Perguntas Frequentes

P: Por que o Rust tem dois tipos de strings? R: Para equilibrar propriedade e desempenho. &str é um empréstimo somente leitura (sem sobrecarga), enquanto String é uma string mutável de propriedade (com sobrecarga de alocação na pilha). Usar &str para parâmetros de função oferece maior flexibilidade, já que String pode ser automaticamente convertido para &str.

P: Como se faz a conversão entre String e &str? R: Para converter &str em String, use .to_string() ou String::from(); para converter String em &str, use &s ou s.as_str(). Todas as conversões têm custo zero (elas simplesmente representam a memória sob uma perspectiva diferente).

P: Por que s[0] não consegue acessar o primeiro caractere? R: Porque o UTF-8 é uma codificação de comprimento variável. s[0] acessa o primeiro byte, que não é necessariamente um caractere completo. O Rust foi projetado para ser mais seguro: ele prefere gerar um erro em tempo de compilação a permitir um comportamento inesperado em tempo de execução.

P: Devo usar + ou format! para concatenar strings? R: Use + para concatenar um número pequeno de strings e format! para vários segmentos. + consome a String à esquerda e reutiliza seu buffer (para alto desempenho), enquanto format! não consome a variável, mas cria uma String totalmente nova. Use + para dois ou três segmentos e format! para mais de três.

P: As strings podem ser modificadas? O que pode ser modificado, &str ou String? R: Apenas String pode ser modificado (após declarar mut, você pode usar métodos como .push_str(), .push(), .insert(), etc.). &str é uma referência somente leitura; os dados aos quais ela aponta não podem ser modificados. Se você precisar modificar uma string dentro de uma função, o tipo do parâmetro deve ser &mut String.


📖 Resumo


📝 Exercícios

  1. Dificuldade ⭐: Crie um String e, em seguida, chame os operadores .push_str(), .push(), + e format! em sequência, comparando os resultados das quatro abordagens.
  2. Dificuldade ⭐⭐: Escreva uma função fn first_word(s: &str) -> &str que retorne a primeira palavra de uma sequência de caracteres (separada por espaços). Chame-a usando &str e String, respectivamente.
  3. Dificuldade ⭐⭐⭐: Explore problemas de limites do UTF-8 — crie uma string contendo o emoji 🦀 "Rust🦀", tente dividi-la com &s[0..5] e &s[0..6] e observe qual delas funciona e qual causa uma falha.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%