Bindings
const MAX: i64 = 1024; // compile-time constant, module level
let x = 1; // a local
let y: f64 = 2.0;
let n = 0; // the same binding: a local that is written is mutable
n = n + 1;
n += 1;A mutabilidade é inferida, e let é a única palavra-chave de binding
Se um nome pode ser reatribuído, e se o valor que ele guarda pode ser alterado no lugar (p.x = 1, xs.push(v), x.bump()), decorre do que o corpo faz com ele: uma variável local que é escrita é mutável, e uma que não é escrita não é. let declara uma variável local, e não existe uma segunda grafia para registrar a intenção, porque uma anotação que o compilador consegue calcular não é pedida.
let x = 1;
x = 2; // accepted: `x` is written, so `x` is mutable
let xs: Array<i64> = [];
xs.push(1); // a method that edits its receiver needs a place, and a binding is oneO que a mutabilidade não é:
- Uma
constnão é uma variável local e nunca pode ser atribuída. - Um lugar alcançado através de uma view é somente leitura, diga o binding o que disser.
- Um parâmetro segue o seu modo: um parâmetro que a clause não nomeia nem em
editsnem emtakesé somente leitura, um parâmetro que a clause dizedits(edits selfem um método) pode ser escrito, e um parâmetro simples que o corpo escreve é umedits, escrito ou inferido do corpo. - Um valor que passou por move ou foi tomado não pode ser escrito pelo nome antigo.
- Um lugar alcançado através de um
sharedé somente leitura, porque duas tasks poderiam escrevê-lo ao mesmo tempo. O lugar que um lock empresta, umLocked<T>, pode ser escrito pelo tempo de uma chamada: veja concorrência.
Um binding que nada escreve é aceito: a mutabilidade decorre do corpo, e uma variável local não usada é assunto da análise de declarações não usadas.
const: calculada quando o programa é compilado
const vive no nível do module e o seu valor é calculado em tempo de compilação, então o que todo uso lê é um literal, qualquer que seja a forma como o inicializador foi escrito.
const fn buckets(bits: i64): i64 {
let base = 1 << bits;
if base > 8 { base } else { 8 }
}
const BUCKETS: i64 = buckets(12); // 4096, computed here
const MASK: i64 = BUCKETS - 1; // a constant may mention another
const READY: bool = BUCKETS > 100;
fn main(): i32 {
let n = const { buckets(4) * 2 }; // computed where a program needs one
n as i32
}As regras:
- Uma
const fné uma função que o compilador consegue rodar em tempo de compilação. Ela também é uma função comum: chamá-la em tempo de execução não muda nada. - O que o evaluator guarda é o que um valor pode ser em tempo de compilação e nada que aloque: inteiros, bools, chars e literais de string. Aritmética, comparação, os operadores lógicos,
as,if,let,return, o valor final de um bloco, outra constante pelo nome e uma chamada a umaconst fn. const { expr }calcula um valor onde um programa precisa de um.- Uma
const fncujo corpo contém qualquer outra coisa - um loop, um índice, uma atribuição a uma variável local, ummatch, um literal que aloca, uma chamada de método, uma chamada a uma função que não éconst fn, uma closure, um template - é recusada na sua declaração, quer algo a chame ou não. - A avaliação em tempo de compilação tem um espaço limitado, então uma
const fnque chama a si mesma sem fim é um erro quando o programa é compilado, e não um compilador que lock. - Uma constante que estoura, divide por zero ou desloca além da largura do seu tipo é um erro em tempo de compilação: o tempo de compilação não tem um panic a que recorrer.
- Não existem const generics:
Array<T>é dinâmico, então nada na linguagem tem um tamanho para parametrizar.
Convenções
Não são impostas, mas a árvore inteira as segue: snake_case para funções, variáveis e modules; PascalCase para tipos, traits e os casos de uma variant; SCREAMING_SNAKE para constantes.