Em Rust, todo valor possui um _tipo de dado_ específico que informa ao compilador como interpretar e trabalhar com esses dados. Rust é uma linguagem _estaticamente tipada_, o que significa que todos os tipos de variáveis devem ser conhecidos em tempo de compilação. Embora o compilador frequentemente infira os tipos, anotações de tipo explícitas (por exemplo, `: u32`) são às vezes necessárias, especialmente quando existe ambiguidade (como com o método `parse`). Os tipos de dados de Rust são amplamente categorizados em dois subconjuntos: escalares e compostos. # Tipos Escalares Tipos escalares representam um único valor. Rust possui quatro tipos escalares primários: ## 1. Inteiros Números sem um componente fracionário. | Comprimento | Com Sinal | Sem Sinal | | ----------- | --------- | --------- | | 8-bit | `i8` | `u8` | | 16-bit | `i16` | `u16` | | 32-bit | `i32` | `u32` | | 64-bit | `i64` | `u64` | | 128-bit | `i128` | `u128` | | arch | `isize` | `usize` | * **Com Sinal vs. Sem Sinal:** * **Com Sinal (`i` prefixo):** Pode armazenar números negativos ou positivos (por exemplo, `i8`, `i16`, `i32`, `i64`, `i128`). Armazenado usando complemento de dois. * **Sem Sinal (`u` prefixo):** Pode armazenar apenas números positivos (por exemplo, `u8`, `u16`, `u32`, `u64`, `u128`). * **Dependente da Arquitetura:** Os tipos `isize` e `usize` correspondem ao tamanho do ponteiro da arquitetura (32 ou 64 bits) e são comumente usados para indexação de coleções. * **Padrão:** `i32` é o tipo inteiro padrão. * **Literais:** Podem ser expressos em várias bases e com separadores visuais |Literais numéricos|Exemplo| |---|---| |Decimal|`98_222`| |Hexadecimal|`0xff`| |Octal|`0o77`| |Binário|`0b1111_0000`| |Byte (`u8` apenas)|`b'A'`| * **Estouro de Inteiro (Integer Overflow):** * No modo de depuração (debug mode), o estouro causa um _panic_ em tempo de execução. * No modo de lançamento (release mode), Rust realiza o _wrapping de complemento de dois_ (por exemplo, `256` para `u8` torna-se `0`). * Métodos de tratamento explícito estão disponíveis: `wrapping_*`, `checked_*`, `overflowing_*`, `saturating_*`. ## 2. Números de Ponto Flutuante Números com casas decimais. * **Tipos:** `f32` (32-bit) e `f64` (64-bit). * **Padrão:** `f64` é o padrão devido à sua maior precisão e velocidade comparável em CPUs modernas. * Todos os tipos de ponto flutuante são com sinal e aderem ao padrão IEEE-754. ## 3. Booleanos Representam valores de verdade. * **Tipo:** `bool`. * **Valores:** `true` ou `false`. * **Tamanho:** Um byte. * Usado principalmente em lógica condicional (por exemplo, expressões `if`). ## 4. Caracteres O tipo alfabético mais primitivo de Rust. * **Tipo:** `char`. * **Literais:** Definidos com aspas simples (por exemplo, `'A'`, `'😻'`). * **Tamanho:** Quatro bytes. * Representa um Valor Escalar Unicode, permitindo uma ampla gama de caracteres além do ASCII básico. ## Operações Numéricas Rust suporta operações matemáticas padrão para todos os tipos numéricos: * Adição: `+` * Subtração: `-` * Multiplicação: `*` * Divisão: `/` (divisão inteira trunca em direção a zero) * Resto: `%` # Tipos Compostos Tipos compostos agrupam múltiplos valores em um único tipo. Rust possui dois tipos compostos primitivos: ## 1. Tuplas Uma forma de propósito geral para agrupar um número fixo de valores, que podem ser de tipos _diferentes_. * **Declaração:** Criadas com uma lista de valores separados por vírgulas dentro de parênteses. ```rust let tup: (i32, f64, u8) = (500, 6.4, 1); ``` * **Acessando Valores:** * **Desestruturação:** Usando um padrão com `let` para extrair valores em variáveis separadas. ```rust let tup = (500, 6.4, 1); let (x, y, z) = tup; // x=500, y=6.4, z=1 ``` * **Indexação:** Usando um ponto (`.`) seguido pelo índice baseado em zero. ```rust let five_hundred = tup.0; // 500 ``` * **Tipo Unitário:** A tupla `()` é chamada de tipo _unitário_, representando um valor vazio ou um tipo de retorno vazio. ## 2. Arrays Uma coleção de valores onde _cada elemento deve ter o mesmo tipo_ e o array tem um _comprimento fixo_. * **Declaração:** Os valores são listados dentro de colchetes. ```rust let a = [1, 2, 3, 4, 5]; ``` * **Anotação de Tipo:** `[Tipo; Comprimento]` ```rust let a: [i32; 5] = [1, 2, 3, 4, 5]; ``` * **Inicialização com Valor Repetido:** `[valor; comprimento]` ```rust let a = [3; 5]; // Equivalente a [3, 3, 3, 3, 3] ``` * **Alocação de Memória:** Arrays são alocados na pilha. Para coleções que precisam crescer ou diminuir, o tipo `Vec` (vetor) é geralmente preferido. * **Acessando Elementos:** Usando indexação baseada em zero. ```rust let first = a[0]; // 1 ``` * **Segurança em Tempo de Execução:** Rust realiza verificações em tempo de execução para acesso a arrays fora dos limites. Tentar acessar um índice maior ou igual ao comprimento do array fará com que o programa entre em _panic_, prevenindo acesso inválido à memória e mantendo os princípios de segurança de memória de Rust. **:: Referência ::** [Tipos de Dados - A Linguagem de Programação Rust](https://doc.rust-lang.org/book/ch03-02-data-types.html) ## Exemplos ```rust fn main() { // Números inteiros em Rust let inteiro: i32 = 42; // Declarando um número inteiro de 32 bits println!("O número inteiro é: {}", inteiro); // Imprime o valor do inteiro // Temos os seguintes tipos de números inteiros: // i8, i16, i32, i64, i128 e isize // E os tipos correspondentes sem sinal: u8, u16, u32, u64, u128 e usize let inteiro_unsigned: u32 = 100; // Declarando um número inteiro sem sinal de 32 bits println!("O número inteiro sem sinal é: {}", inteiro_unsigned); // Imprime o valor // Números de ponto flutuante em Rust let ponto_flutuante: f64 = 3.14; // Declarando um número de ponto flutuante de 64 bits println!("O número de ponto flutuante é: {}", ponto_flutuante); // // Temos os tipos f32 e f64 para números de ponto flutuante let ponto_flutuante_f32: f32 = 2.718; // Declarando um número de ponto flutuante de 32 bits println!("O número de ponto flutuante f32 é: {}", ponto_flutuante_f32); // Imprime o valor // Boleanos let verdadeiro: bool = true; // Declarando um valor booleano verdadeiro let falso: bool = false; // Declarando um valor booleano falso println!("O valor verdadeiro é: {}", verdadeiro); // Imprime o valor verdadeiro println!("O valor falso é: {}", falso); // Imprime o valor falso // Caracteres let caracter: char = 'R'; // Declarando um caractere println!("O caractere é: {}", caracter); // Imprime o caractere // Temos os tipos char para caracteres Unicode let emoji: char = '😊'; // Declarando um emoji como caractere println!("O emoji é: {}", emoji); // Imprime o emoji // Strings let string: &str = "Olá, Rust!"; // Declarando uma string como uma fatia de string println!("A string é: {}", string); // Imprime a string // Temos o tipo String para strings mutáveis let mut string_mutavel: String = String::from("Olá, mundo!"); // Declarando uma string mutável string_mutavel.push_str(" Bem-vindo ao Rust!"); // Adicionando texto à string mutável println!("A string mutável é: {}", string_mutavel); // Imprime a string mutável // Tuplas let tupla: (i32, f64, char) = (42, 3.14, 'R'); // Declarando uma tupla com diferentes tipos println!("A tupla é: {:?}", tupla); // Imprime a tupla // Podemos acessar os elementos da tupla usando a notação de índice println!("O primeiro elemento da tupla é: {}", tupla.0); // Acessa o primeiro elemento println!("O segundo elemento da tupla é: {}", tupla.1); // Acessa o segundo elemento println!("O terceiro elemento da tupla é: {}", tupla.2); // Acessa o terceiro elemento // Arrays let array: [i32; 3] = [1, 2, 3]; // Declarando um array de inteiros com tamanho fixo println!("O array é: {:?}", array); // Imprime o array // Podemos acessar os elementos do array usando a notação de índice println!("O primeiro elemento do array é: {}", array[0]); // Acessa o primeiro elemento println!("O segundo elemento do array é: {}", array[1]); // Acessa o segundo elemento println!("O terceiro elemento do array é: {}", array[2]); // Acessa o terceiro elemento } ```