Generics and type inference
This topic is not available in the selected language or version. Showing the start of this section.
Declare a type parameter with <T> to reuse code that stores or passes values of different types. Save this as generics.xen:
struct Pair<T> { first: T, second: T }
fn identity<T>(value: T) -> T { return value;}
fn main() { let pair = Pair<Int> { first: identity(7), second: 9 }; println(pair.first); println(identity<String>("hello"));}$ xen run generics.xen7helloIn identity(7), the argument determines T. identity<String>("hello") supplies it explicitly. For several parameters, provide all types as <A, B>. Structs and enums can also have type parameters. Methods for a generic type use impl<T> Pair<T> { ... }.
When a type is unknown
Section titled “When a type is unknown”An empty vector or Option.None does not identify an item type by itself. Write let items: Vec<Int> = []; or let missing: Option<String> = Option.None;.
Generic function inference uses argument types, not the type of the variable receiving the result. let n: I32 = identity(1); fails because 1 selects Int. Use identity<I32>(1) or identity(i32(1)). Partial type argument lists are unsupported.
What can be reused?
Section titled “What can be reused?”A generic function can store, pass, and return values and work with known shapes such as Option<T> or Vec<T>. It cannot directly apply +, comparisons, or arbitrary fields and methods to an unknown T. For example, a + b inside fn add<T>(a: T, b: T) -> T is rejected. Use a concrete numeric type such as Int, or pass a named function callback that performs the required operation. Traits, bounds, and operator overloading are unavailable.
A generic function can match an enum or tuple whose shape is already known. It cannot match an unknown T against number literals or a particular enum.
Work with a known shape
Section titled “Work with a known shape”Even when T is unknown, an Option<T> has known Some and None variants. Save this as generic_shapes.xen. or_default selects a value without arithmetic, and a method on Pair<T> reads its declared field.
struct Pair<T> { first: T, second: T }impl<T> Pair<T> { fn left(&self) -> T { return self.first; }}fn or_default<T>(value: Option<T>, fallback: T) -> T { return match value { Option.Some(item) => item, Option.None => fallback, };}fn main() { let pair = Pair<Int> { first: 7, second: 9 }; println(pair.left()); let missing: Option<String> = Option.None; println(or_default(missing, "untitled")); println(or_default(Option.Some(42), 0));}xen check generic_shapes.xenxen run generic_shapes.xen7untitled42In or_default(missing, "untitled"), both arguments use the same T, namely String. Some returns its item; None returns the fallback. The fallback is evaluated before the function call. Use match at the call site if evaluating it would be expensive or have unwanted side effects.
A receiving type does not drive inference
Section titled “A receiving type does not drive inference”This program deliberately fails under xen check generic_inference_error.xen:
fn identity<T>(value: T) -> T { return value; }fn main() { let value: I32 = identity(1); println(value); }expected I32, found Int (I64)identity(1) chooses Int from the argument 1. The I32 on the left does not change that choice. Specify identity<I32>(1), or convert the argument first with identity(i32(1)).
A type parameter alone does not define operations
Section titled “A type parameter alone does not define operations”This program also deliberately fails under xen check generic_add_error.xen:
fn add<T>(left: T, right: T) -> T { return left + right;}fn main() { println(add(1, 2)); }operation requires a concrete typeT might be an integer or a type without addition. This release has no traits, bounds, or operator overloading. If integer addition is the task, declare Int parameters. For configurable transformations, pass a concrete named function as a callback, as shown in slices and iterators.
Generic declarations do not remove copy and move rules. Consult copies, moves, and references for the behavior of the actual type.
Try it
Section titled “Try it”Fix identity(1) in both ways described above. Explain why passing different types to or_default cannot satisfy its single T.