Tópicos avançados em Tipos

Clube do Livro #19

Padrão NewType

  • Legibilidade do código: evita confusão ao introduzir um novo tipo que o compilador nos obriga a usar
struct Meters(u32);
struct SquareMeters(u32);
fn area(width: Meters, height: Meters) -> SquareMeters {
    //
}
  • Legibilidade do código: tipos explícitos para os parâmetros facilitam uso e manutenção
  • Uso especial de uma estrutura-tupla (tupple struct)

Clube do Livro #19

Padrão NewType

use std::collections::HashMap;

#[derive(Debug)]
struct People(HashMap<usize, String>);

impl People {
    fn new() -> Self {
        return Self(HashMap::new());
    }

    fn add(&mut self, name: String) -> bool {
        let does_contain = self.0.values().any(|val| *val == name);
        if !does_contain {
            self.0.insert(self.0.len() + 1, name);
        }
        !does_contain
    }
}

fn main() {
    let mut people = People::new();
    people.add(String::from("Fulano"));
    let result2 = people.add(String::from("Fulano"));
    assert_eq!(false, result2);

    people.add(String::from("Ciclano"));
    people.add(String::from("Beltrano"));
    println!("{:#?}", people)
}
  • Nos permite abstrair (simplificar ou ocultar) o funcionamento de um tipo mais complexo:

Clube do Livro #19

Sinônimos (type)

  • Apelidos para tipos já definidos
  • Podem ser simples ou compostos:
// um simples apelido para o tipo i32
type Kilometers = i32;

// um caso mais complexo de um trait object com lifetime
type Thunk = Box<dyn Fn() + Send + 'static>;
  • Principal uso: redução de repetição de código, indicação clara do tipo a ser usado (manutenabilidade), interface comum
  • Não é um tipo diferente como acontece no padrão NewType

Clube do Livro #19

Sinônimos (type)

use std::fmt;
use std::io::Error;

pub trait Write {
    fn write(&mut self, buf: &[u8]) -> Result<usize, Error>;
    fn flush(&mut self) -> Result<(), Error>;

    fn write_all(&mut self, buf: &[u8]) -> Result<(), Error>;
    fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<(), Error>;
}

// O mesmo código com o sinônimo:

type Result<T> = std::result::Result<T, std::io::Error>;


pub trait Write {
    fn write(&mut self, buf: &[u8]) -> Result<usize>;
    fn flush(&mut self) -> Result<()>;

    fn write_all(&mut self, buf: &[u8]) -> Result<()>;
    fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<()>;
}

Clube do Livro #19

O tipo que nunca retorna (!)

  • Tipo especial no rust para indicar uma função que nunca retorna, isto é, uma função divergente
use std::process::exit;
fn the_answer() -> ! {
    exit(42);
}
  • Sofre coerção para qualquer outro tipo (análise sintática):
let guess: u32 = match guess.trim().parse() {
	Ok(num) => num,
	Err(_) => continue,
};

Clube do Livro #19

Trait sized e DSTs

  • Dynamically sized types (DST): tipos com tamanho dinamicamente alocado: tamanho dos tipos somente pode ser determinado em tempo de execução
  • Sempre são implementados com ponteiros
  • O exemplo mais clássico é o &str
fn main() {
    let s1: str = "Hello there!";
    let s2: str = "How's it going?";
}
  • É impossível criar uma variável que guarde um DST
  • Em Rust DSTs sempre são criados através de ponteiros inteligentes: uma estrutura que guarda o tamanho dos dados e um ponteiro para os dados na heap

Clube do Livro #19

Trait sized e DSTs

  • Traits são DSTs que podem ser referenciadas pelo nome
  • O Rust tem uma trait chamada Sized, que é implementada automaticamente em todos os tipos cujo tamanho sabemos em tempo de compilação
  • Além disso, Rust adiciona essa trait automaticamente (trait bound) a todas as funções que lidam com genéricos:
fn generic<T>(t: T) {
    // código
}

// é o mesmo que:

fn generic<T: Sized>(t: T) {
    // código
}

Clube do Livro #19

Trait sized e DSTs

  • Por padrão, funções genéricas somente trabalharão com tipos que implementam a trait Sized, mas você pode relaxar esse requisito com o código abaixo:
fn generic<T: ?Sized>(t: &T) {
    // código
}
  • O código acima indica que T pode ser um tipo com tamanho conhecido ou não em tempo de compilação. Admitindo a segunda possibilidade, neste caso usamos uma referência (um tipo de ponteiro) para o tipo, pois DSTs sempre são acessadas através de ponteiros

Clube do Livro #19

Obrigado, pessoal!

@felubra

twitter, github, telegram:

Referências

Clube do Livro #19

Advanced Types

By Felipe Lube de Bragança

Advanced Types

Material de apoio para apresentação do capítulo 15 do Livro do Rust em nosso canal do youtube, dia 24/01/2021 às 21h.

  • 60