Genéricos
Nesta página
Funções genéricas recebem um ou mais parâmetros de tipo entre <>. O
compilador infere T a partir dos argumentos na chamada — não é preciso
especificar o tipo explicitamente. Funções com múltiplos parâmetros de tipo
(<A, B>) preservam cada tipo de forma independente:
identity<T>, head<T> e pair_first<A, B> funcionam com int, str, bool e arrays.
// Feature: Generic functions
// Syntax: `fn name<T>(x: T) -> T { ... }`
// When to use: logic that works with ANY type (identity, containers,
// swaps) preserving the concrete type through the return value.
// Identity — `T` is inferred at the call site.
fn identity<T>(x: T) -> T {
return x
}
print(identity(42)) // 42
print(identity("hi")) // hi
print(identity(true)) // true
// More than one arg of the same `T`.
fn first<T>(a: T, b: T) -> T {
return a
}
print(first(1, 2)) // 1
print(first("a", "b")) // a
// Generic with array.
fn head<T>(arr: [T]) -> T {
return arr[0]
}
print(head([10, 20, 30])) // 10
print(head(["x", "y"])) // x
// Multiple type params.
fn pair_first<A, B>(a: A, b: B) -> A {
return a
}
print(pair_first(7, "right-side")) // 7
// expected:
// 42
// hi
// true
// 1
// a
// 10
// x
// 7
Structs genéricos armazenam qualquer tipo sem perder a informação de qual
é. O tipo concreto é inferido do literal de construção. O bloco impl de um
struct genérico acompanha o parâmetro de tipo:
Box<T>, Pair<A, B> e Stack<T> com impl Stack incluindo método associado e de instância.
// Feature: Generic structs
// Syntax: `struct Name<T> { field: T }`
// When to use: container that holds any type while preserving it.
struct Box<T> {
value: T,
}
// Concrete type comes from usage.
let bi = Box { value: 42 } // Box<int>
let bs = Box { value: "hello" } // Box<str>
print(bi.value) // 42
print(bs.value) // hello
// Multiple type parameters.
struct Pair<A, B> {
first: A,
second: B,
}
let p = Pair { first: 1, second: "one" }
print(p.first) // 1
print(p.second) // one
// `impl` for generic struct — the `<T>` is inferred from the type name.
// NOTE: `len` is dispatched at the runtime level for Array/Map, so we
// expose the size under a distinct name (`size`) to avoid the clash.
struct Stack<T> {
items: [T],
}
impl Stack {
fn make() -> Stack {
return Stack { items: [] }
}
fn size(self) -> int {
return self.items.len()
}
}
let s = Stack::make()
print(s.size()) // 0
Enums genéricos são a base de tipos somados parametrizados — o mesmo
mecanismo que a stdlib usa para Option<T> e Result<T, E>. O match
desestrutura o payload e liga a variável ao tipo concreto:
Maybe<T> com variantes Just(T) e Nothing; Either<L, R> com dois parâmetros.
// Feature: Generic enums
// Syntax: `enum Name<T> { Variant(T), Other }`
// When to use: parameterized sum types — this is how the stdlib
// defines `Option<T>` and `Result<T, E>`.
enum Maybe<T> {
Just(T),
Nothing,
}
// Construction: the `T` type comes from the payload.
let just_int = Maybe.Just(42)
let just_str = Maybe.Just("hello")
let none: Maybe<int> = Maybe.Nothing
let r1 = match just_int {
Maybe::Just(v) => "got {v}",
Maybe::Nothing => "none",
}
print(r1) // got 42
let r2 = match just_str {
Maybe::Just(v) => "got '{v}'",
Maybe::Nothing => "none",
}
print(r2) // got 'hello'
let r3 = match none {
Maybe::Just(v) => "got {v}",
Maybe::Nothing => "none",
}
print(r3) // none
// Result<T, E> with two type parameters.
enum Either<L, R> {
Left(L),
Right(R),
}
let lf = Either.Left("error")
let rt = Either.Right(99)
print(match lf {
Either::Left(s) => "L:{s}",
Either::Right(n) => "R:{n}",
})
// L:error
print(match rt {
Either::Left(s) => "L:{s}",
Either::Right(n) => "R:{n}",
})
// R:99
Desafio
Escreva uma função genérica swap<A, B>(a: A, b: B) -> Pair<B, A> que devolve
os valores na ordem trocada. Use o struct Pair do exemplo de structs genéricos.
Veja também