You've written code that only works for one type. Generics let you write it once and have it work for many — without giving up Rust's compile-time type safety.
The problem generics solve
Imagine a Point with two i32 coordinates. Now you need one with f64 coordinates. Copy-pasting the struct with different types is the duplication generics exist to kill. A generic parameter T stands in for 'some type chosen later,' and the compiler stamps out a specialized version for each concrete type you actually use.
Generic structs and methods
struct Point<T> { x: T, y: T } works for any type T. Methods go in impl<T> Point<T>. At each use site, Point<i32> and Point<f64> are distinct, fully type-checked types. There's no runtime cost: generics are monomorphized — the compiler generates concrete code per type, so it runs exactly as fast as if you'd hand-written each version.
The limit, and what comes next
A bare T can be stored and moved, but you can't do much with it — you can't add two Ts or print one, because not every type supports those. To say 'T must be addable' or 'T must be printable,' you need trait bounds — and traits are the entire next track. Generics and traits are two halves of one idea; you've just met the first half.