Pular para o conteúdo

O livro · 11

Interfaces

book.md · 62 linhas · 2 min de leitura

Objetivo: entender satisfação estrutural (sem implements), o dispatch por vtable, e as interfaces pequenas que costuram a stdlib.

Uma interface é um decl com assinaturas (capítulo 03). Implementar os métodos já satisfaz: não há declaração implements:

decl Writeable {
fn write(data: []byte) -> error
}
decl Log { ... }
fn (l: *Log) write(data: []byte) -> error { ... } // Log agora satisfaz Writeable, sem dizer nada

Um valor de interface carrega um par (ponteiro pro dado, ponteiro pra vtable do tipo concreto). A vtable é montada pelo compilador por tipo, não por instância: a struct nunca carrega ponteiros de função inline. É o custo do par no ponto de uso, zero overhead por objeto:

fn log_to(w: Writeable, msg: string) -> error { // 'w' é um valor de interface (vtable)
return w.write(msg.bytes)
}

Quando o resultado depende do tipo concreto (map[A,B](xs: C[A]) -> C[B]), aí é generic monomorfizado, não vtable (capítulo 10). A régua: ação que não devolve o tipo do receiver pode usar vtable; transformação que devolve precisa de monomorfização.

Makoto adota “small composable interfaces”: poucas, de um ou dois métodos, que tudo reusa:

  • Readable (io): fn read(buf: mut []byte) -> Result[usize, error], Ok(0) = EOF.
  • Writeable (io): fn write(data: []byte) -> error.
  • Display (fmt): fn to_string() -> string; é o que a interpolação {{ }} usa.
  • Iterator[T] (io): fn next() -> Optional[T]; é o que loop x in it consome.
  • Serializable (encoding): vira bytes e volta; auto-derivada por reflect (capítulo 09/10).

Porque são pequenas, compõem: qualquer Readable pode ser bufferizado, copiado (io.copy), escaneado por linha; qualquer Display pode ser interpolado; qualquer Iterator pode ser map/filter/ collect. Você implementa um método e ganha o ecossistema.

sc := io.scanner(io.stdin())
loop linha in sc.lines() { process(linha) } // stdin é Readable → scanner → Iterator[string]

Hash/Ord/Clone/Drop/From não existem como interfaces. Hashabilidade e ordem são derivadas (por reflect) ou funções (fn(T,T) -> Order); cópia é @copy-free default e @promote; cleanup é defer. O eixo de comparação/cópia não vira família de traits.

Próximo: 12 · Módulos e ecossistema