Introdução
Neste laboratório, exploraremos o conceito de strings em Rust. Rust possui dois tipos de strings: String e &str.
Uma String é uma string alocada no heap (heap-allocated), expansível, que garante ser uma sequência UTF-8 válida. Por outro lado, &str é uma fatia (slice) que aponta para uma sequência UTF-8 válida e pode ser usada para visualizar uma String.
Em Rust, literais de string podem ser escritos de diferentes maneiras, incluindo o uso de escapes para representar caracteres especiais. Por exemplo, \x3F representa o caractere ponto de interrogação e \u{211D} representa um ponto de código Unicode. Literais de string raw (raw string literals) também podem ser usados se você quiser escrever uma string exatamente como está, sem escapes.
Se você precisar trabalhar com strings de bytes, Rust fornece literais de string de bytes usando o prefixo b. Strings de bytes podem ter escapes de bytes, mas não escapes Unicode. Strings de bytes raw (raw byte strings) também podem ser usadas de maneira semelhante aos literais de string raw.
É importante notar que str e String devem sempre ser sequências UTF-8 válidas. Se você precisar trabalhar com strings em diferentes codificações, pode usar crates externos como encoding para conversões entre codificações de caracteres.
Nota: Se o laboratório não especificar um nome de arquivo, você pode usar qualquer nome de arquivo que desejar. Por exemplo, você pode usar
main.rs, compilar e executá-lo comrustc main.rs && ./main.
Strings
Existem dois tipos de strings em Rust: String e &str.
Uma String é armazenada como um vetor de bytes (Vec<u8>), mas garante sempre ser uma sequência UTF-8 válida. String é alocada no heap (heap allocated), expansível e não é terminada em nulo.
&str é uma fatia (slice) (&[u8]) que sempre aponta para uma sequência UTF-8 válida e pode ser usada para visualizar uma String, assim como &[T] é uma visualização de Vec<T>.
fn main() {
// (todas as anotações de tipo são supérfluas)
// Uma referência a uma string alocada em memória somente leitura
let pangram: &'static str = "the quick brown fox jumps over the lazy dog";
println!("Pangrama: {}", pangram);
// Iterar sobre as palavras em ordem inversa, nenhuma nova string é alocada
println!("Palavras em ordem inversa");
for word in pangram.split_whitespace().rev() {
println!("> {}", word);
}
// Copiar caracteres em um vetor, ordenar e remover duplicatas
let mut chars: Vec<char> = pangram.chars().collect();
chars.sort();
chars.dedup();
// Criar uma `String` vazia e expansível
let mut string = String::new();
for c in chars {
// Inserir um char no final da string
string.push(c);
// Inserir uma string no final da string
string.push_str(", ");
}
// A string aparada é uma fatia da string original, portanto, nenhuma nova
// alocação é realizada
let chars_to_trim: &[char] = &[' ', ','];
let trimmed_str: &str = string.trim_matches(chars_to_trim);
println!("Caracteres usados: {}", trimmed_str);
// Alocar uma string no heap
let alice = String::from("I like dogs");
// Alocar nova memória e armazenar a string modificada lá
let bob: String = alice.replace("dog", "cat");
println!("Alice diz: {}", alice);
println!("Bob diz: {}", bob);
}
Mais métodos str/String podem ser encontrados nos módulos std::str e std::string
Literais e escapes
Existem várias maneiras de escrever literais de string com caracteres especiais neles. Todos resultam em um &str semelhante, então é melhor usar a forma que for mais conveniente para escrever. Da mesma forma, existem várias maneiras de escrever literais de string de bytes, que resultam em &[u8; N].
Geralmente, caracteres especiais são escapados com uma barra invertida: \. Desta forma, você pode adicionar qualquer caractere à sua string, mesmo aqueles que não podem ser impressos e aqueles que você não sabe como digitar. Se você quiser uma barra invertida literal, escape-a com outra: \\
Delimitadores de string ou literais de caractere que ocorrem dentro de um literal devem ser escapados: "\"", '\''.
fn main() {
// Você pode usar escapes para escrever bytes por seus valores hexadecimais...
let byte_escape = "I'm writing \x52\x75\x73\x74!";
println!("O que você está fazendo\x3F (\\x3F significa ?) {}", byte_escape);
// ...ou pontos de código Unicode.
let unicode_codepoint = "\u{211D}";
let character_name = "\"DOUBLE-STRUCK CAPITAL R\"";
println!("Caractere Unicode {} (U+211D) é chamado de {}",
unicode_codepoint, character_name );
let long_string = "Literais de string
podem abranger várias linhas.
A quebra de linha e a indentação aqui ->\
<- também podem ser escapadas!";
println!("{}", long_string);
}
Às vezes, há muitos caracteres que precisam ser escapados ou é muito mais conveniente escrever uma string como está. É aqui que os literais de string raw entram em jogo.
fn main() {
let raw_str = r"Escapes don't work here: \x3F \u{211D}";
println!("{}", raw_str);
// Se você precisar de aspas em uma string raw, adicione um par de #s
let quotes = r#"And then I said: "There is no escape!""#;
println!("{}", quotes);
// Se você precisar de "#" em sua string, basta usar mais #s no delimitador.
// Você pode usar até 65535 #s.
let longer_delimiter = r###"A string with "## in it. And even "##!"###;
println!("{}", longer_delimiter);
}
Quer uma string que não seja UTF-8? (Lembre-se, str e String devem ser UTF-8 válidos). Ou talvez você queira um array de bytes que seja principalmente texto? Strings de bytes para o resgate!
use std::str;
fn main() {
// Observe que esta não é realmente uma `&str`
let bytestring: &[u8; 21] = b"this is a byte string";
// Arrays de bytes não têm o trait `Display`, então imprimi-los é um pouco limitado
println!("Uma string de bytes: {:?}", bytestring);
// Strings de bytes podem ter escapes de bytes...
let escaped = b"\x52\x75\x73\x74 as bytes";
// ...mas sem escapes unicode
// let escaped = b"\u{211D} is not allowed";
println!("Alguns bytes escapados: {:?}", escaped);
// Strings de bytes raw funcionam como strings raw
let raw_bytestring = br"\u{211D} is not escaped here";
println!("{:?}", raw_bytestring);
// Converter um array de bytes para `str` pode falhar
if let Ok(my_str) = str::from_utf8(raw_bytestring) {
println!("E o mesmo como texto: '{}'", my_str);
}
let _quotes = br#"You can also use "fancier" formatting, \
like with normal raw strings"#;
// Strings de bytes não precisam ser UTF-8
let shift_jis = b"\x82\xe6\x82\xa8\x82\xb1\x82\xbb"; // "ようこそ" em SHIFT-JIS
// Mas então elas nem sempre podem ser convertidas para `str`
match str::from_utf8(shift_jis) {
Ok(my_str) => println!("Conversão bem-sucedida: '{}'", my_str),
Err(e) => println!("Conversão falhou: {:?}", e),
};
}
Para conversões entre codificações de caracteres, consulte o crate encoding.
Uma listagem mais detalhada das maneiras de escrever literais de string e caracteres de escape é fornecida no capítulo 'Tokens' da Rust Reference.
Resumo
Parabéns! Você concluiu o laboratório de Strings. Você pode praticar mais laboratórios no LabEx para aprimorar suas habilidades.