10
Generic
Our list only holds i32. Generics in Rust are like C++ templates: a type parameter T that the compiler stamps out a fresh copy of the code for at each use site. Monomorphization, same as std::vector<T>.
- Add
<T>to every type and impl. The struct, the node, the link alias, the impl block header. The body of each method already speaks in terms of the field types, so almost nothing else changes.pub struct List<T> { head: Link<T>, } type Link<T> = Option<Box<Node<T>>>; struct Node<T> { elem: T, next: Link<T>, } impl<T> List<T> { pub fn new() -> Self { List { head: None } } } -
pushandpopneedTplumbed through their signatures. Note theimpl<T>on the block — that introduces the parameter for everything inside, andSelfalready meansList<T>.impl<T> List<T> { pub fn push(&mut self, elem: T) { let new_node = Box::new(Node { elem, next: self.head.take(), }); self.head = Some(new_node); } pub fn pop(&mut self) -> Option<T> { self.head.take().map(|node| { self.head = node.next; node.elem }) } } - Generics aren't free at the binary level —
List<i32>andList<String>produce two separate copies of every method. That's monomorphization, and it's why Rust generics are zero-overhead at runtime but can balloon code size if you go wild.let mut a: List<i32> = List::new(); let mut b: List<String> = List::new(); a.push(1); b.push("hello".to_string());