Generics
Overview
Section titled “Overview”Generics allow writing code that works with multiple types while maintaining type safety. Ard uses a simple syntax for generic types that doesn’t require explicit declarations.
Generic Syntax
Section titled “Generic Syntax”Generic types begin with $ in function and struct declarations:
fn map(list: [$A], transform: fn($A) $B) [$B] { mut result: [$B] = [] for item in list { result.push(transform(item)) } result}In this example, $A and $B are generic type parameters. The function accepts a list of type $A and returns a list of type $B.
Type Inference
Section titled “Type Inference”The compiler attempts to infer generic types from usage:
fn identity(value: $T) $T { value}
let number = identity(42) // $T inferred as Intlet text = identity("hello") // $T inferred as Strlet flag = identity(true) // $T inferred as BoolExplicit Type Arguments
Section titled “Explicit Type Arguments”When type inference isn’t sufficient, provide explicit type arguments:
let ints = [1, 2, 3]let floats = map<Int, Float>(ints, Float::from_int)Type arguments correspond to the order of generics introduced in the signature.
Generic Structs
Section titled “Generic Structs”Structs can also hold generics. If a generic type appears in a field, that field introduces the struct’s generic parameter:
struct Container { value: $T,}
let int_container = Container{value: 42}let str_container = Container{value: "hello"}When a generic parameter is used only by methods or other type-level behavior, declare it explicitly on the struct:
struct State<$T> { handle: StateHandle,}
impl State { fn value() $T { panic("not implemented") }}When referencing a generic struct as a type, provide concrete type arguments:
fn get_value(container: Container<Int>) Int { container.value}