Tipos de dados em Rust

Tipos de dados em Rust

Neste artigo, aprenda sobre inteiros, pontos flutuantes, caracteres e tipos de dados booleanos na linguagem de programação Rust.

Em um artigo anterior sobre a linguagem de programação Rust, analisamos variáveis, constantes e sombreamento.

É natural cobrir tipos de dados agora.

O que são tipos de dados?

O computador armazena dados como 0s e 1s, mas para dar sentido a eles ao ler, usamos o tipo de dados para dizer o que esses 0s e 1s significam.

Rust tem dois tipos de tipos de dados:

  1. Tipo de dados escalares: Tipos que armazenam apenas um único valor.
  2. Tipo de dados compostos: Tipos que armazenam vários valores, até mesmo valores de tipos diferentes.

Neste artigo, abordaremos os tipos de dados escalares. Vamos passar pela segunda categoria no próximo artigo.

A seguir está uma breve visão geral das quatro principais categorias de tipos de dados escalares no Rust:

  • Inteiros: Armazena números inteiros. Tem subtipos para cada caso de uso específico.
  • Ponto flutuante: Armazena números com um valor fracionário. Tem dois subtipos com base no tamanho.
  • Caracteres: Armazena um único caractere de codificação UTF-8. (Sim, você pode armazenar um emoji em um caractere.)
  • Booleanos: Armazena um true ou um false. (Para desenvolvedores que não podem concordar se 0 é true ou se 0 significa false.)

Números Inteiros

Um inteiro no contexto de uma linguagem de programação refere-se a números naturais, não fracionários. Os inteiros em Rust são Signed (com sinal) ou Unsigned (sem sinal). Inteiros sem sinal armazenam apenas 0 e números positivos, enquanto inteiros com sinal podem armazenar números negativos, 0 e números positivos.

O intervalo de inteiros assinados começa a partir de -(2n-1) e esse intervalo termina com (2n-1)-1. Da mesma forma, o intervalo para inteiros sem sinal começa em 0 e termina com (2n)-1.A seguir estão os tipos inteiros disponíveis com base no sinal e no tamanho:

tabela_tipos

Como você pode ver, Rust tem inteiros com sinal e sem sinal de comprimento 8, 16, 32, 64 e até 128!

Os números inteiros com *size variam de acordo com a arquitetura do computador. Em microcontroladores de 8 bits, é *8, em computadores legados de 32 bits, é *32 e em sistemas modernos de 64 bits, é *64.

O uso de *size é armazenar dados que estão principalmente relacionados à memória (que é dependente da máquina), como ponteiros, deslocamentos, etc.

Quando você não especifica explicitamente um subconjunto do tipo Inteiro, o compilador Rust inferirá que seu tipo será i32 por padrão. Obviamente, se o valor for maior ou menor do que o que i32 pode conter, o compilador do Rust educadamente errará e pedirá que você anote manualmente o tipo.


Rust não só permite que você armazene inteiros em sua forma decimal, mas também nas formas binária, octal e hexadecimal.

Para uma melhor legibilidade, você pode usar sublinhado _ como um substituto para vírgulas na escrita/leitura de grandes números.

fn main() {
    let bin_value = 0b100_0101; // usando o prefixo '0b' para representação binária
    let oct_value = 0o105; // usando o prefixo '0o' para octais
    let hex_value = 0x45; // usando o prefixo '0x' for hexadecimais
    let dec_value = 1_00_00_000; // o mesmo que escrever 1 Crore (1,00,00,000)

    println!("bin_value: {bin_value}");
    println!("oct_value: {oct_value}");
    println!("hex_value: {hex_value}");
    println!("dec_value: {dec_value}");
}

Armazenamos o número decimal 69 na forma binária, forma octal e forma hexadecimal nas variáveis bin_value, oct_value e hex_value respectivamente. Na variável dec_value, armazenamos o número 1 Crore (10 milhões) e tenho vírgulas com sublinhados, de acordo com o sistema de numeração indiano. Para aqueles mais familiarizados com o sistema de numeração internacional, você pode escrever isso como 10_000_000.

Ao compilar e executar esse binário, obtenho a seguinte saída:

bin_value: 69
oct_value: 69
hex_value: 69
dec_value: 10000000

Números de ponto flutuante

Números de ponto flutuante, ou mais comumente conhecido como "float(s)" é um tipo de dados que contém números que têm um valor fracionário (algo após o ponto decimal).

Ao contrário do tipo Inteiro em Rust, os números de ponto flutuante têm apenas dois tipos de subconjunto:

  • f32: Tipo de ponto flutuante de precisão única
  • f64: Tipo de ponto flutuante de precisão dupla

Como o tipo Inteiro em Rust, quando Rust infere o tipo de uma variável que parece um ponto flutuante, ele recebe o tipo f64. Isso ocorre porque o tipo f64 tem mais precisão do que o tipo f32 e é quase tão rápido quanto o tipo f32 na maioria das operações computacionais. Observe que ambos os tipos de dados de ponto flutuante (f32 e f64) são com sinal.

A linguagem de programação Rust armazena os números de ponto flutuante de acordo com o padrão IEEE 754 de representação numérica de ponto flutuante e aritmética.

fn main() {
    let pi: f32 = 3.1400; // f32
    let golden_ratio = 1.610000; // f64
    let five = 5.00; // o ponto decimal indica que deve ser um tipo de ponto flutuante
    let six: f64 = 6.; // mesmo que esteja explícito, o ponto decimal ainda é **necessário**

    println!("pi: {pi}");
    println!("golden_ratio: {golden_ratio}");
    println!("five: {five}");
    println!("six: {six}");
}

Olhe atentamente para a 4ª linha. Mesmo que eu tenha anotado o tipo para a variável six, precisamos pelo menos usar o ponto decimal. Se você tem algo depois o ponto decimal é com você.

A saída deste programa é bastante previsível.

pi: 3.14
golden_ratio: 1.61
five: 5
six: 6

Na saída acima, você deve ter notado que, ao exibir o valor armazenado dentro das variáveis pi, golden_ratio e five, os zeros à direita que especificamos no momento da declaração da variável estão ausentes.

Embora esses zeros não sejam removidos, eles são omitidos ao emitir os valores por meio da macro println. Então não, o Rust não mexeu nos valores da sua variável.

Caracteres

Você pode armazenar um único caractere em uma variável e o tipo é simplesmente char. Como as linguagens de programação tradicionais dos anos 80, você pode armazenar um caractere ASCII. Mas Rust também estende o tipo de caractere para armazenar um caractere UTF-8 válido. Isso significa que você pode armazenar um emoji em um único caractere.

Alguns emojis são uma mistura de dois emojis existentes. Um bom exemplo é o emoji 'Fiery Heart': ❤️‍🔥 . Este emoji é construído combinando dois emojis usando um caractere de largura zero: ❤️ + 🔥 = ❤️‍🔥

Armazenar tais emojis em uma única variável Rust do tipo de caractere não é possível.

fn main() {
    let a = 'um';
    let p: char = 'p'; // com o tipo explícito para caractere
    let crab = '🦀';

    println!("Ah veja, {} {}! :{}", a, crab, p);
}

Como você pode ver, armazenamos os caracteres ASCII 'a' e 'p' dentro das variáveis a e p. Também armazenamos um caractere UTF-8 válido, o emoji de caranguejo, na variável crab. Em seguida, imprimimos os caracteres armazenados em cada uma dessas variáveis.

A seguir está a saída:

Ah, veja, um 🦀! :p

Booleanos

O tipo booleano em Rust armazena apenas um dos dois valores possíveis: true ou false. Se desejar anotar o tipo, use bool para indicar o tipo.

fn main() {
    let val_t: bool = true;
    let val_f = false;

    println!("val_t: {val_t}");
    println!("val_f: {val_f}");
}

O código acima, quando compilado e executado, resulta na seguinte saída:

val_t: true
val_f: false

Bônus: Conversão de tipos explícita

No artigo anterior sobre Variáveis na linguagem de programação Rust, mostramos um programa de conversão de temperatura bem básico. Lá, mencionamos que Rust não permite conversão de tipos implícita.

Mas isso não significa que Rust também não permita conversão de tipos explícita.

Para executar a conversão de tipo explícita, a palavra-chave as é usada e seguida pelo tipo de dados para o qual o valor deve ser convertido.

A seguir está um programa de demonstração:

fn main() {
    let a = 3 as f64; // f64
    let b = 3.14159265359 as i32; // i32

    println!("a: {a}");
    println!("b: {b}");
}

Na linha 2, em vez de usar '3.0', sigo o '3' com as f64 para indicar que quero que o compilador manipule a conversão de tipos de '3' (um Integer) em um float de 64 bits. O mesmo com a 3ª linha. Mas aqui, o tipo de casting é lossy. Ou seja, que o elemento fracionário desapareceu completamente. Em vez de armazenar 3.14159265359, ele é armazenado como simplesmente 3.

Isso pode ser verificado a partir da saída do programa:

a: 3
b: 3

Conclusão

Este artigo aborda os tipos de dados primitivos/escalares no Rust. Existem basicamente quatro desses tipos de dados: Inteiros, Números de ponto flutuante, Caracteres e Booleanos.

Inteiros são usados para armazenar números naturais e eles têm vários subtipos com base em se são com sinal ou sem sinal e o comprimento. Os números de ponto flutuante são usados para armazenar números com alguns valores fracionários e têm dois subtipos com base no comprimento. O tipo de dados de caractere é usado para armazenar um único caractere codificado UTF-8 válido. Finalmente, os booleanos são usados para armazenar um valor true ou false.

Em outro artigo, discutimos tipos de dados compostos como arranjos e énuplos (tuplas).

Via itsfoss.com. Você pode conferir o post original em inglês:

Rust Basics Series #3: Data Types in Rust

Última atualização deste artigo: 4 de january de 2024