Skip to Content
Errors, Traits & Generics

Errors, Traits & Generics

Error handling — Result<T, E>, not exceptions

Rust splits errors in two:

  • Unrecoverablepanic! (bugs, broken invariants). Unwinds/aborts. Like an uncaught exception, but you don’t design around catching it.
  • RecoverableResult<T, E>, an ordinary value you must handle.
enum Result<T, E> { Ok(T), Err(E) }
Kotlin — exceptions
fun read(): String { /* throws IOException */ } try { val s = read() use(s) } catch (e: IOException) { ... }
Rust — Result
fn read() -> Result<String, io::Error> { ... } match read() { Ok(s) => use_it(s), Err(e) => eprintln!("{e}"), }

The ? operator is the killer feature — it’s “unwrap-or-return-the-error”, turning verbose matching into a clean happy path:

fn load() -> Result<Config, io::Error> { let text = fs::read_to_string("config.toml")?; // if Err, return it now let cfg = parse(&text)?; // same Ok(cfg) }

? is roughly like Java’s checked-exception propagation (which Kotlin deliberately dropped) — except the error is an ordinary value, visible and type-checked at every call site. Crates like anyhow (apps) and thiserror (libraries) make error types ergonomic.

Traits — interfaces, extensions, and generic bounds in one

Rust has no inheritance. trait is the single tool for shared behavior — it’s Kotlin’s interface (with default methods), extension functions, and operator overloading combined.

// Kotlin interface with default method trait Greet { fn name(&self) -> String; // required fn hello(&self) -> String { // default method format!("Hello, {}", self.name()) } } struct Dog; impl Greet for Dog { fn name(&self) -> String { "Rex".into() } }

Because traits can be implemented for any type (even ones you didn’t define, like adding a method to i32), they double as extension functions:

trait Doubler { fn double(&self) -> Self; } impl Doubler for i32 { fn double(&self) -> i32 { self * 2 } } let x = 21.double(); // 42

Common derivable/standard traits worth knowing early: Debug, Clone, Copy, PartialEq/Eq, Default, From/Into, Iterator, Display.

Generics

Nearly identical to Kotlin, with where-clause bounds:

// Kotlin: fun <T : Comparable<T>> max(list: List<T>): T fn largest<T: PartialOrd + Copy>(list: &[T]) -> T { let mut max = list[0]; for &item in list { if item > max { max = item; } } max } // longer form fn print_all<T>(items: &[T]) where T: std::fmt::Display { /* ... */ }
  • Trait bounds (T: PartialOrd) are Kotlin’s generic constraints (T : Comparable<T>).
  • impl Trait in argument/return position ≈ Kotlin’s use of an interface type without naming the generic.
  • Rust generics are monomorphized (zero-cost, specialized per type at compile time) — no reflection, no type erasure.
Last updated on