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
}
```