Tipos e bindings
Em Python ou JavaScript, x = 7 simplesmente funciona: a linguagem descobre em tempo de execução que x é um número. O Glide é diferente. Todo valor tem um tipo que o compilador conhece antes do programa rodar, e você geralmente escreve o tipo ao lado do nome. A recompensa por ser explícito é que o compilador consegue pegar uma enorme classe de bugs — passar uma string onde você queria um número, ler um campo que não existe — antes de você colocar o código em produção.
Bindings: let, mut, const
A coisa mais simples que você pode fazer é vincular um valor a um nome:
let x: i32 = 42;
Leia isso como "que exista uma variável chamada x do tipo i32 com o valor 42." i32 é abreviação de "inteiro de 32 bits com sinal" — mais sobre larguras em um momento.
Por padrão, bindings com let são imutáveis: uma vez que você atribuiu o valor, ele não pode mudar.
let x: i32 = 42;
x = 7; // ✗ erro: `x` não é mutável
Se você precisar mesmo mudar o valor depois, adicione mut:
let mut count: i32 = 0;
count = count + 1; // ✓ count é mutável
count = count + 1;
const é para valores fixos em tempo de compilação — conhecidos antes mesmo de o programa começar a rodar:
const MAX_RETRIES: i32 = 5;
const GREETING: string = "hello";
O compilador embute esses valores no binário. Eles não podem depender de entrada do usuário, conteúdo de arquivos ou qualquer outra coisa que só é conhecida em tempo de execução.
Inferência de tipos
Você pode omitir o tipo quando o lado direito deixa isso óbvio:
let n = 42; // inferido como i32
let pi = 3.14; // inferido como f64
let name = "glide"; // inferido como string
Mas nas assinaturas de funções (parâmetros e tipo de retorno), os tipos são obrigatórios. O raciocínio é: assinaturas são documentação; surpresas não ajudam. Você verá isso no próximo capítulo.
Inteiros, especificados pela largura
O Glide não oferece um int genérico. Você escolhe uma largura que corresponda ao que o valor vai armazenar:
let a: i8 = -100; // -128 .. 127
let b: i16 = 30_000; // ±32K
let c: i32 = 2_000_000_000; // ±2 bilhões — o padrão habitual
let d: i64 = 9_000_000_000; // ±9 quintilhões
let e: i128 = 170_141_183_460_469_231_731_687_303_715_884_105_727;
let p: u8 = 255; // sem sinal: 0 .. 255 (sem negativos)
let q: u16 = 65535;
let r: u32 = 4_000_000_000;
let s: u64 = 18_000_000_000_000_000_000;
O i significa com sinal (pode ser negativo), o u significa sem sinal (apenas zero e acima). O número é a largura em bits — um número maior = intervalo maior = mais memória usada por valor.
Sublinhados em literais numéricos são apenas um recurso visual — 1_000_000 é o mesmo que 1000000. Literais podem ser decimais, hexadecimais ou binários:
let mask: u32 = 0xFFFF_0000; // hex
let bits: u8 = 0b1010_1100; // binário
let count: i32 = 1_000_000; // decimal simples
Não há conversões implícitas de redução de largura. Para ir de i64 para i32, você escreve a conversão explicitamente com as:
let big: i64 = 100;
let small: i32 = big as i32;
Floats
Duas larguras: f32 (cerca de 7 dígitos decimais de precisão) e f64 (cerca de 15). f64 é o padrão para literais decimais sem anotação — é o que você vai querer quase sempre. (float é um alias que você encontrará em alguns códigos; equivale a f64.)
let g: f32 = 1.5;
let h: f64 = 2.718281828;
O Glide não converte inteiros para floats automaticamente. let x: f64 = 1; é um erro — escreva 1.0 ou 1 as f64.
Booleanos
bool armazena true ou false. É um tipo de verdade, distinto de inteiros — if 1 { ... } é rejeitado pelo compilador.
let ready: bool = true;
let done: bool = false;
if ready && !done {
println!("go");
}
Os operadores são && (e), || (ou), ! (não).
Strings
string é uma sequência de bytes UTF-8 — o tipo de texto. Literais entre aspas duplas com as sequências de escape usuais (\n, \t, \", \\):
let s: string = "hello, glide";
println!(s);
println!("length:", s.len());
println!("upper:", s.to_upper());
println!("first 5:", s.substring(0, 5));
Dentro de um literal, ${expr} interpola uma expressão — o mesmo padrão dos template literals do JavaScript:
let n: i32 = 7;
let msg: string = "got ${n} results";
Alguns métodos de string que você vai usar bastante: .len(), .eq(other), .contains(sub), .starts_with(p), .ends_with(s), .trim(), .to_upper(), .to_lower(), .split(sep), .replace(find, repl), .substring(start, end), .index_of(sub), .concat(other).
Ponteiros
É aqui que o Glide começa a se diferenciar do Python de forma visível. Um ponteiro é um valor que armazena o endereço de outro valor. O tipo *T significa "ponteiro para um T."
let x: i32 = 42;
let p: *i32 = &x; // & significa "obtenha o endereço de"
println!(*p); // * significa "siga o ponteiro" → 42
Você não vai escrever &x e *p com frequência para escalares. Onde ponteiros aparecem em todo lugar é em objetos alocados no heap — valores que vivem no heap porque crescem em tempo de execução:
let v: *Vector<i32> = Vector::new();
v.push(1);
v.push(2);
Aqui, Vector::new() aloca um novo vector no heap e devolve um ponteiro para ele. Você pode chamar métodos diretamente pelo ponteiro (v.push(...)); o Glide descobre sozinho quando desreferenciar.
Modelo de memória é o capítulo inteiro dedicado a explicar como isso funciona sem precisar de um coletor de lixo. Por ora: qualquer valor criado com Type::new() vive no heap, e o Glide rastreia quem tem a posse dele e insere a limpeza correta automaticamente.
Structs
Um struct é um conjunto fixo de campos nomeados empacotados juntos. Você define o tipo uma vez e constrói valores com a sintaxe de literal de struct:
struct Point {
x: f64,
y: f64,
}
fn main() -> i32 {
let origin: Point = Point { x: 0.0, y: 0.0 };
let p: Point = Point { x: 3.0, y: 4.0 };
let dist: f64 = (p.x * p.x + p.y * p.y);
println!("distance² from origin:", dist);
return 0;
}
Acesse campos com .campo. Se você já usou dataclasses do Python, structs parecem algo familiar — exceto que cada campo tem um tipo, e você não pode adicionar um campo que não estava na declaração.
Vectors
Vector<T> é uma lista expansível de valores, todos do tipo T. Como uma lista do Python, mas com todos os elementos do mesmo tipo. Ele vive no heap e tem a posse do seu conteúdo:
let v: *Vector<i32> = Vector::new();
v.push(1);
v.push(2);
v.push(3);
println!("len:", v.len());
println!("first:", v.get(0));
for x in v {
println!(x);
}
A macro push_all! insere vários valores em uma linha — útil para popular um vector rapidamente:
let primes: *Vector<i32> = Vector::new();
primes.push_all!(2, 3, 5, 7, 11);
HashMaps
HashMap<V> é uma tabela hash com chaves do tipo string. Os valores podem ser de qualquer tipo V:
let h: *HashMap<i32> = HashMap::new();
h.insert("alice", 30);
h.insert("bob", 25);
if h.contains("alice") {
println!("alice is", h.get("alice"));
}
Para chaves que não são strings, você recorre às variantes tipadas em stdlib::hashmap — voltaremos a isso quando precisarmos.
Um primeiro olhar em ?T e !T
Mais dois tipos que você vai ver em todo lugar no código Glide, abordados em profundidade em Erros como valores:
- `?T` — "talvez um T". Ou
some(value)ounone(). Use quando uma função pode não ter nada para retornar. - `!T` — "um T ou uma mensagem de erro". Ou
ok(value)ouerr("message"). Use quando uma operação pode falhar.
fn first_byte(s: string) -> ?i32 {
if s.len() == 0 { return none(); }
return some(s.at(0).to_int());
}
fn parse(s: string) -> !i32 {
if s.eq("") { return err("empty input"); }
return ok(42);
}
Para onde ir agora
Os valores estão catalogados. A seguir você vai movimentá-los com Funções e fluxo de controle.