Ir para o conteúdo

Bindings ​

talor
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.

talor
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 one

O que a mutabilidade não é:

  • Uma const nã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 edits nem em takes é somente leitura, um parâmetro que a clause diz edits (edits self em um método) pode ser escrito, e um parâmetro simples que o corpo escreve é um edits, 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, um Locked<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.

talor
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 uma const fn.
  • const { expr } calcula um valor onde um programa precisa de um.
  • Uma const fn cujo corpo contém qualquer outra coisa - um loop, um índice, uma atribuição a uma variável local, um match, 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 fn que 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.

Talor v0.1.0 - Publicado sob a licença MIT OR Apache-2.0.