https://www.brandons.me/blog/polymorphism-in-rust

1. Enums

enum Shape {
	Rectangle { width: f32, height: f32 },
	Triangle { side: f32 },
	Circle { radius: f32 },
}

impl Shape {
	pub fn perimeter(&self) -> f32 {
		match self {
			Shape::Rectangle { width, height } => width * 2.0 + height * 2.0,
			Shape::Triangle { side } => side * 3.0,
			Shape::Circle { radius } => radius * 2.0 * std::f32::consts::PI
		}
	}
	
	pub fn area(&self) -> f32 {
		match self {
			Shape::Rectangle { width, height } => width * height,
			Shape::Triangle { side } => side * 0.5 * 3.0_f32.sqrt() / 2.0 * side,
			Shape::Circle { radius } => radius * radius * std::f32::consts::PI
		}
	}
}

fn print_area(shape: Shape) {
	println!("{}", shape.area());
}

fn print_perimeters(shapes: Vec<Shape>) {
	for shape in shapes.iter() {
		println!("{}", shape.perimeter());
	}
}

2. Traits

trait Shape {
	fn perimeter(&self) -> f32;
	fn area(&self) -> f32;
}

struct Rectangle {
	pub width: f32,
	pub height: f32
}

struct Triangle {
	pub side: f32
}

struct Circle {
	pub radius: f32
}

impl Shape for Rectangle {
	fn perimeter(&self) -> f32 {
		self.width * 2.0 + self.height * 2.0
	}
	
	fn area(&self) -> f32 {
		self.width * self.height
	}
}

impl Shape for Triangle {
	fn perimeter(&self) -> f32 {
		self.side * 3.0
	}
	
	fn area(&self) -> f32 {
		self.side * 0.5 * 3.0_f32.sqrt() / 2.0 * self.side
	}
}

impl Shape for Circle {
	fn perimeter(&self) -> f32 {
		self.radius * 2.0 * std::f32::consts::PI
	}
	
	fn area(&self) -> f32 {
		self.radius * self.radius * std::f32::consts::PI
	}
}

// Generics
fn print_area<S: Shape>(shape: S) {
	println!("{}", shape.area());
}

fn print_perimeters<S: Shape>(shapes: Vec<S>) {
	for shape in shapes.iter() {
		println!("{}", shape.perimeter());
	}
}

// Dynamics
fn print_area(shape: &dyn Shape) {
	println!("{}", shape.area());
}

fn print_perimeters(shapes: Vec<&dyn Shape>) {
	for shape in shapes.iter() {
		println!("{}", shape.perimeter());
	}
}

Mix

enum ShapeEnum {
	Rectangle(Rectangle),
	Triangle(Triangle),
	Circle(Circle)
}

struct Rectangle {
	pub width: f32,
	pub height: f32
}

struct Triangle {
	pub side: f32
}

struct Circle {
	pub radius: f32
}

trait Shape {
	fn perimeter(&self) -> f32;
	fn area(&self) -> f32;
}

impl Shape for ShapeEnum {
	fn perimeter(&self) -> f32 {
		match self {
			ShapeEnum::Rectangle(rect) => rect.perimeter(),
			ShapeEnum::Triangle(tri) => tri.perimeter(),
			ShapeEnum::Circle(circ) => circ.perimeter(),
		}
	}
	fn area(&self) -> f32 {
		match self {
			ShapeEnum::Rectangle(rect) => rect.area(),
			ShapeEnum::Triangle(tri) => tri.area(),
			ShapeEnum::Circle(circ) => circ.area(),
		}
	}
}

impl Shape for Rectangle {
	fn perimeter(&self) -> f32 {
		self.width * 2.0 + self.height * 2.0
	}
	fn area(&self) -> f32 {
		self.width * self.height
	}
}

impl Shape for Triangle {
	fn perimeter(&self) -> f32 {
		self.side * 3.0
	}
	fn area(&self) -> f32 {
		self.side * 0.5 * 3.0_f32.sqrt() / 2.0 * self.side
	}
}

impl Shape for Circle {
	fn perimeter(&self) -> f32 {
		self.radius * 2.0 * std::f32::consts::PI 
	}
	fn area(&self) -> f32 {
		self.radius * self.radius * std::f32::consts::PI
	}
}

Comparison

Inline layout No wasted memory Mixed-type collections Extensibile Easy to write and maintain
Enums βœ… ❌ βœ… ❌ βœ…
Generics βœ… βœ… ❌ βœ… βœ…
Dynamic ❌ βœ… βœ… βœ… βœ…
Mix βœ… βœ… βœ… βœ… ❌