Generics & Type Parameters
Generics allow you to write reusable algorithms and data structures that work across multiple concrete data types while preserving Flame’s static type safety, ownership guarantees, and zero-cost abstractions.
Generic Functions
Section titled “Generic Functions”Declare generic functions by placing type parameters inside angle brackets <T> after the function name:
// Single type parameterfn identity<T>(x: T) -> T { return x}
// Multiple type parametersfn pair<A, B>(first: A, second: B) -> (A, B) { return (first, second)}
let num = identity(42) // Type inferred as Intlet greeting = identity("Flame") // Type inferred as StringGeneric Structs
Section titled “Generic Structs”Structs can store generic fields, allowing unified layouts for collections, wrappers, and containers:
// Single generic parameterstruct Box<T> { value: T}
// Multiple generic parametersstruct KeyValuePair<K, V> { key: K, value: V}
let int_box = Box { value: 100 }let str_box = Box { value: "hello world" }
let entry = KeyValuePair { key: "port", value: 8080 }Generic Impl Blocks
Section titled “Generic Impl Blocks”To add methods to a generic struct, declare the generic parameters on impl and apply them to the target type:
impl<T> Box<T> { // Consume self and return inner value fn unwrap(self) -> T { return self.value }
// Borrow reference to inner value fn get(&self) -> &T { return &self.value }}
let b = Box { value: 42 }print(b.unwrap()) // 42Generic Enums
Section titled “Generic Enums”Enums frequently use generics to represent variant payloads that vary by type, such as Option<T> or Result<T, E>:
enum MyOption<T> { Some(T), None,}
let opt_int = MyOption.Some(99)let opt_str = MyOption.Some("active")
match opt_int { MyOption.Some(v) => print($"Value: {v}"), MyOption.None => print("No value"),}Ownership, References & Borrow Semantics
Section titled “Ownership, References & Borrow Semantics”Generics in Flame follow the same ownership, borrow checking, and mutation semantics as concrete types:
| Syntax | Semantics | Description |
|---|---|---|
x: T |
Value (Move) | Ownership is transferred into the function or struct. |
x: &T |
Immutable Reference | Read-only borrow; multiple immutable borrows can coexist simultaneously. |
x: &mut T |
Mutable Reference | Exclusive borrow; allows updating the underlying data without reallocating. |
struct Container<T> { item: T}
impl<T> Container<T> { // Read-only inspection fn inspect(&self) { // self.item is borrowed immutably as &T }
// In-place mutation fn replace(&mut self, new_item: T) -> T { let old = self.item self.item = new_item return old }}Trait Bounds: <T: Trait>
Section titled “Trait Bounds: <T: Trait>”You can restrict generic parameters to types that satisfy specific trait contracts:
trait Drawable { fn draw(&self) -> String}
// T must implement Drawablefn render<T: Drawable>(shape: &T) { print(shape.draw())}Multiple trait bounds can be combined using +:
trait Describable { fn describe(&self) -> String}
// T must satisfy both Drawable and Describablefn inspect_and_draw<T: Drawable + Describable>(item: &T) { print(item.describe()) print(item.draw())}