Pular para o conteúdo

O que a linguagem garante

Segurança de tipos, de memória, limites de índice, ausência segura, dados imutáveis e padrões seguros — com a medida, e com o que fica de fora.

Boa parte da segurança de um sistema é decidida antes de qualquer biblioteca: pelo que a linguagem torna impossível. Esta página é o inventário honesto disso — o que é garantido, o que é verificado, e o que continua por conta de quem escreve.

Segurança de memória#

A classe de falha mais cara da história do software — estouro de buffer, uso após liberação, ponteiro pendurado — não existe aqui, e não por mérito da linguagem: o interpretador roda sobre o CPython, que gerencia a memória. Não há aritmética de ponteiro no caminho comum, não há free, e todo acesso a coleção é conferido.

Classe de falhaEstadoPorque
estouro de bufferimpossível no caminho comumcoleções crescem; índice é conferido
uso após liberaçãoimpossívelnão há liberação manual
ponteiro penduradoimpossívela contagem de referências segura o objeto
dupla liberaçãoimpossívelidem
corrida de dadospossívelthreads compartilham memória e nada é automático
dataforge
adopt Arcane.Estrutura as Est

bloco := Est.bloco(16)

// O limite e conferido — e o erro tem NOME, e nao um valor de lixo.
monitor:
    Cabecalho := Est.definir("Cabecalho", [["a", "u64"], ["b", "u64"],
        ["c", "u64"]], ordem := "rede")
    Cabecalho.ler(bloco)
    assert no
handle BufferOverflowError as e:
    out "recusado: o registro nao cabe no bloco"

// E o ponteiro nulo tambem:
p := Est.nulo()
monitor:
    p.ler()
    assert no
handle NullPointerError as e:
    out "recusado: ponteiro nulo"

Segurança de tipos#

A linguagem é dinamicamente tipada com verificação estática opcional. A distinção importa: o tipo não é apagado em execução — ele é conferido —, e o check prova antes de rodar o que consegue provar.

ConfereQuandoCódigo
tipo declarado de variável e parâmetroexecução e checkx: Integer := "a"
aridade da chamada, inclusive entre arquivoscheckP.criar(1, 2, 3)
campo que não existe num record ou instânciacheck, com sugestãop.clientte
índice fora do alcance num literal fixocheckindice-fora-do-alcance
chave ausente num vault literalcheck, com sugestãochave-ausente
limite de um genérico <T extends X>ambosgeneric-bound
refinamento de um type … wheretoda fronteiratipo-refinado
dataforge
// O refinamento vale em TODA fronteira — declaracao, parametro,
// retorno e campo. Um tipo que so valesse na criacao seria uma
// sugestao, e nao um tipo.
type Positivo := Integer where valor bigger 0

action dividir(total: Integer, partes: Positivo) -> Integer:
    yield total ~/ partes

assert dividir(10, 2) is 5

monitor:
    dividir(10, 0)
    assert no
handle Error as e:
    out "recusado na fronteira: 0 nao e Positivo"

// E o tipo OPACO e o que impede confundir dois textos. A regra
// vale na CRIACAO, entao um valor invalido nunca existe:
opaque type Cpf := String where len(valor) is 11

cpf := Cpf("12345678909")
assert cpf isnt void

monitor:
    Cpf("123")
    assert no
handle Error as e:
    out "recusado: um Cpf tem 11 digitos"

out "o tipo opaco separa dois textos que parecem iguais"

Ausência segura#

void é um valor, não um ponteiro nulo — e ler um campo de void levanta NullReferenceError, com linha e coluna, em vez de corromper qualquer coisa. Os dois operadores que evitam o given defensivo são os mesmos do resto do mundo.

dataforge
v := {"nome": "Ana"}

// '??' da o padrao quando o valor e void.
assert (v["idade"] ?? 0) is 0

// '?.' para a cadeia em vez de levantar.
endereco := void
assert endereco?.cidade is void

// E a distincao que o analisador conhece: depois de conferir,
// o tipo deixa de incluir void.
given v["nome"] isnt void:
    assert len(v["nome"]) is 3

out "ausencia tratada, e nao adivinhada"

Dados imutáveis#

Imutabilidade é um controle de segurança, e não só de estilo: um valor que não muda não pode ser alterado por outra parte do programa depois de conferido — o padrão TOCTOU (conferir e usar) simplesmente não acontece.

FormaO que ela garante
steadyo nome não é reatribuído
recordcampos imutáveis, igualdade estrutural, with devolve cópia
freeze(xs)um cluster que não muda
Objetos.congelar(obj)a instância recusa escrita
Dom.valor(...)objeto de valor com a regra cobrada na criação
dataforge
record Permissao:
    papel: String
    acao: String

p := Permissao("editor", "escrever")

// Conferido uma vez, vale para sempre: nada muda o objeto depois.
monitor:
    p.papel := "admin"
    assert no
handle Error as e:
    out "recusado: record e imutavel"

// 'with' devolve um NOVO — o original continua o que era.
elevada := p with {"papel": "admin"}
assert p.papel is "editor"
assert elevada.papel is "admin"

Padrões seguros — a lista#

PadrãoA alternativa insegura que ele evita
a indentação só aceita espaçoo mesmo arquivo com significados diferentes
// é comentário; divisão inteira é ~/ambiguidade em código lido às pressas
record é imutável por padrãocompartilhar estado sem perceber
campo com padrão mutável é copiado no spawntodas as instâncias dividirem a mesma lista
monitor sem handle não engole o erroa falha sumir silenciosamente
defer não engole erroum arquivo não fechado terminar com código 0
parallel levanta o erro da tarefa que falhouum CI verde com metade do trabalho perdida
Crypto.hash_password usa scryptSHA-256 puro numa senha
o check avisa sobre escrita concorrenteperder atualizações em silêncio

Limites e recursos#

A disponibilidade tem controles próprios na linguagem, e eles existem porque o caso comum é acidente e não ataque.

LimiteO que ele impede
teto de mil quadros de chamadarecursão infinita comer a pilha (e yield f(…) vira salto)
Seg.json_seguroaninhamento fundo estourar a pilha do leitor
Kiln.limite_de_corpoum corpo gigante consumir a memória
teto na fila do Arcane.Lacofonte mais rápida que o consumo morrer por memória
Arcane.Inicio.limite_da_pilhao teto real da thread, e não o presumido