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 0
s e 1
s, mas para dar sentido a eles ao ler, usamos o tipo de dados para dizer o que esses 0
s e 1
s significam.
Rust tem dois tipos de tipos de dados:
- Tipo de dados escalares: Tipos que armazenam apenas um único valor.
- 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 umfalse
. (Para desenvolvedores que não podem concordar se0
étrue
ou se0
significafalse
.)
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:
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 únicaf64
: 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).