Pular para o conteúdo

Newtypes

Nesta página

Um newtype cria um tipo distinto a partir de outro, sem custo em tempo de execução. O compilador recusa misturar UserId(int) com OrderId(int) mesmo que ambos sejam int por baixo — o tipo é o contrato.

IDs distintos, Email com helpers, unidades de medida e fábrica com validação.

11-newtypes-rich.zolo
Playground
// Feature: "Rich" newtypes — opaque wrappers with helpers

// Syntax: `newtype X(T)`; create with `X.new(v)`; extract with `.unwrap()`;

// add behavior via top-level functions that take the

// newtype.

// When to use: strong domain types (Email, UserId, Money) that

// the typechecker refuses to mix with the underlying type.


use std::String

newtype UserId(int)
newtype OrderId(int)

// Distinct IDs: typechecker refuses to swap the order.

fn buy(user: UserId, order: OrderId) {
  print("user {user.unwrap()} bought order {order.unwrap()}")
}

let u = UserId.new(7)
let o = OrderId.new(99)
buy(u, o)

// buy(o, u)  // ERROR at compile-time: incompatible types


// Newtype with helpers — top-level functions that take the newtype.

newtype Email(str)

fn email_local(e: Email) -> str {
  let parts = e.unwrap().split("@")
  return parts[0]
}

fn email_domain(e: Email) -> str {
  let parts = e.unwrap().split("@")
  return parts[1]
}

let e = Email.new("alice@zolo.dev")
print(e.unwrap())  // alice@zolo.dev

print(email_local(e))  // alice

print(email_domain(e))  // zolo.dev


// Newtype for "units" — distinguishes Meters vs Feet.

newtype Meters(float)
newtype Feet(float)

fn meters_to_feet(m: Meters) -> Feet {
  return Feet.new(m.unwrap() * 3.28084)
}

fn feet_to_meters(f: Feet) -> Meters {
  return Meters.new(f.unwrap() / 3.28084)
}

let m = Meters.new(100.0)
let f = meters_to_feet(m)
print(f.unwrap())  // ~328.084


let back = feet_to_meters(f)
print(back.unwrap())  // ~100.0


// Factory with light validation — enforces an invariant.

fn make_user_id(n: int) -> UserId {
  if n < 0 {
    return UserId.new(0)
  }
  return UserId.new(n)
}

let safe = make_user_id(-3)
print(safe.unwrap())  // 0


let ok = make_user_id(42)
print(ok.unwrap())  // 42

Boas práticas:

  • Crie o valor com Tipo.new(v) e extraia com .unwrap().
  • Para adicionar comportamento, escreva funções de topo que recebem o newtype — fn email_local(e: Email) -> str { ... } — em vez de depender de impl sobre newtype, que não é estável.
  • Use newtypes para IDs, unidades (Metros, Pés, Graus) e qualquer valor primitivo que o domínio proíbe de trocar acidentalmente.

Veja também

Buscar no Zolo

9 resultados

enespt-br