📘 PHẦN 5: TRAITS VÀ GENERICS
🎯 Mục tiêu tổng quát
- Master trait definitions và implementations
- Generic programming với type parameters
- Understand trait bounds và where clauses
- Work with trait objects và dynamic dispatch
- Implement common standard library traits
- Build flexible, reusable code with generics
- Create type-safe abstractions
🧑🏫 Bài 1: Traits cơ bản
Defining traits
rust
pub trait Summary {
fn summarize(&self) -> String;
}Implementing traits
rust
pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
fn main() {
let article = NewsArticle {
headline: String::from("Rust 2.0 Released"),
location: String::from("San Francisco"),
author: String::from("John Doe"),
content: String::from("The Rust team is excited to announce..."),
};
let tweet = Tweet {
username: String::from("rust_lang"),
content: String::from("Announcing Rust 2.0!"),
reply: false,
retweet: false,
};
println!("Article: {}", article.summarize());
println!("Tweet: {}", tweet.summarize());
}Default implementations
rust
pub trait Summary {
fn summarize_author(&self) -> String;
fn summarize(&self) -> String {
format!("(Read more from {}...)", self.summarize_author())
}
}
impl Summary for Tweet {
fn summarize_author(&self) -> String {
format!("@{}", self.username)
}
// Can override default or use it
}
fn main() {
let tweet = Tweet {
username: String::from("rust_lang"),
content: String::from("Announcing Rust 2.0!"),
reply: false,
retweet: false,
};
println!("{}", tweet.summarize());
}Trait bounds
rust
pub fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
// Equivalent to:
pub fn notify2<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
fn main() {
let article = NewsArticle {
headline: String::from("Rust 2.0 Released"),
location: String::from("SF"),
author: String::from("John"),
content: String::from("..."),
};
notify(&article);
}Multiple traits
rust
use std::fmt::{Display, Debug};
pub fn notify(item: &(impl Summary + Display)) {
println!("Breaking news! {}", item.summarize());
}
// With generics
pub fn notify2<T: Summary + Display>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
// Where clause (cleaner)
pub fn some_function<T, U>(t: &T, u: &U) -> i32
where
T: Display + Clone,
U: Clone + Debug,
{
// Function body
0
}Returning traits:
rust
fn returns_summarizable() -> impl Summary {
Tweet {
username: String::from("rust_lang"),
content: String::from("Rust is awesome!"),
reply: false,
retweet: false,
}
}
// Can only return one type!
// This won't work:
// fn returns_summarizable(switch: bool) -> impl Summary {
// if switch {
// NewsArticle { ... }
// } else {
// Tweet { ... } // ERROR: different types
// }
// }🧑🏫 Bài 2: Generic Types
Generic functions
rust
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let numbers = vec![34, 50, 25, 100, 65];
println!("Largest: {}", largest(&numbers));
let chars = vec!['y', 'm', 'a', 'q'];
println!("Largest: {}", largest(&chars));
}Generic structs
rust
#[derive(Debug)]
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
// Specific implementation for f32
impl Point<f32> {
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
fn main() {
let integer = Point { x: 5, y: 10 };
let float = Point { x: 1.0, y: 4.0 };
println!("{:?}", integer);
println!("Distance: {}", float.distance_from_origin());
}Multiple type parameters:
rust
#[derive(Debug)]
struct Point<T, U> {
x: T,
y: U,
}
fn main() {
let both_integer = Point { x: 5, y: 10 };
let both_float = Point { x: 1.0, y: 4.0 };
let integer_and_float = Point { x: 5, y: 4.0 };
println!("{:?}", integer_and_float);
}Generic enums
rust
enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}Generic methods
rust
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn new(x: T, y: T) -> Self {
Point { x, y }
}
}
impl<T, U> Point<T, U> {
fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
Point {
x: self.x,
y: other.y,
}
}
}
fn main() {
let p1 = Point::new(5, 10);
let p2 = Point { x: "Hello", y: 'c' };
let p3 = p1.mixup(p2);
println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}Constraints
rust
use std::fmt::Display;
struct Pair<T> {
x: T,
y: T,
}
impl<T> Pair<T> {
fn new(x: T, y: T) -> Self {
Self { x, y }
}
}
impl<T: Display + PartialOrd> Pair<T> {
fn cmp_display(&self) {
if self.x >= self.y {
println!("The largest member is x = {}", self.x);
} else {
println!("The largest member is y = {}", self.y);
}
}
}
fn main() {
let pair = Pair::new(10, 20);
pair.cmp_display();
}🧑🏫 Bài 3: Advanced Traits
Associated types
rust
pub trait Iterator {
type Item; // Associated type
fn next(&mut self) -> Option<Self::Item>;
}
struct Counter {
count: u32,
}
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
self.count += 1;
if self.count < 6 {
Some(self.count)
} else {
None
}
}
}
fn main() {
let mut counter = Counter { count: 0 };
while let Some(value) = counter.next() {
println!("{}", value);
}
}Operator overloading
rust
use std::ops::Add;
#[derive(Debug, PartialEq)]
struct Point {
x: i32,
y: i32,
}
impl Add for Point {
type Output = Point;
fn add(self, other: Point) -> Point {
Point {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 3, y: 4 };
let p3 = p1 + p2;
println!("{:?}", p3);
}Generic Add:
rust
use std::ops::Add;
#[derive(Debug)]
struct Millimeters(u32);
struct Meters(u32);
impl Add<Meters> for Millimeters {
type Output = Millimeters;
fn add(self, other: Meters) -> Millimeters {
Millimeters(self.0 + (other.0 * 1000))
}
}
fn main() {
let mm = Millimeters(1000);
let m = Meters(2);
let total = mm + m;
println!("{:?}", total);
}Supertraits
rust
use std::fmt;
trait OutlinePrint: fmt::Display {
fn outline_print(&self) {
let output = self.to_string();
let len = output.len();
println!("{}", "*".repeat(len + 4));
println!("*{}*", " ".repeat(len + 2));
println!("* {} *", output);
println!("*{}*", " ".repeat(len + 2));
println!("{}", "*".repeat(len + 4));
}
}
struct Point {
x: i32,
y: i32,
}
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
impl OutlinePrint for Point {}
fn main() {
let p = Point { x: 1, y: 3 };
p.outline_print();
}Trait objects
rust
pub trait Draw {
fn draw(&self);
}
pub struct Button {
pub width: u32,
pub height: u32,
pub label: String,
}
impl Draw for Button {
fn draw(&self) {
println!("Drawing button: {}", self.label);
}
}
pub struct SelectBox {
pub width: u32,
pub height: u32,
pub options: Vec<String>,
}
impl Draw for SelectBox {
fn draw(&self) {
println!("Drawing select box with {} options", self.options.len());
}
}
pub struct Screen {
pub components: Vec<Box<dyn Draw>>,
}
impl Screen {
pub fn run(&self) {
for component in self.components.iter() {
component.draw();
}
}
}
fn main() {
let screen = Screen {
components: vec![
Box::new(Button {
width: 50,
height: 10,
label: String::from("OK"),
}),
Box::new(SelectBox {
width: 75,
height: 10,
options: vec![
String::from("Yes"),
String::from("No"),
],
}),
],
};
screen.run();
}Dynamic dispatch
rust
// Static dispatch (compile-time)
fn static_dispatch<T: Draw>(item: &T) {
item.draw();
}
// Dynamic dispatch (runtime)
fn dynamic_dispatch(item: &dyn Draw) {
item.draw();
}
// Trait object in return position
fn get_drawable(choice: bool) -> Box<dyn Draw> {
if choice {
Box::new(Button {
width: 50,
height: 10,
label: String::from("OK"),
})
} else {
Box::new(SelectBox {
width: 75,
height: 10,
options: vec![String::from("Yes")],
})
}
}🧑🏫 Bài 4: Lifetimes với Generics
Generic lifetime parameters
rust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("long string is long");
{
let string2 = String::from("xyz");
let result = longest(string1.as_str(), string2.as_str());
println!("Longest: {}", result);
}
}Lifetime bounds
rust
use std::fmt::Display;
fn longest_with_announcement<'a, T>(
x: &'a str,
y: &'a str,
ann: T,
) -> &'a str
where
T: Display,
{
println!("Announcement! {}", ann);
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let s1 = "hello";
let s2 = "world";
let result = longest_with_announcement(s1, s2, "Comparing strings");
println!("Longest: {}", result);
}Struct lifetimes
rust
#[derive(Debug)]
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl<'a> ImportantExcerpt<'a> {
fn level(&self) -> i32 {
3
}
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("Attention: {}", announcement);
self.part
}
}
fn main() {
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().expect("Could not find a '.'");
let excerpt = ImportantExcerpt {
part: first_sentence,
};
println!("{:?}", excerpt);
println!("Level: {}", excerpt.level());
}Static lifetime
rust
fn main() {
// String literals have 'static lifetime
let s: &'static str = "I have a static lifetime.";
println!("{}", s);
}🧑🏫 Bài 5: Common Traits
Debug và Display
rust
use std::fmt;
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 3, y: 4 };
println!("{:?}", p); // Debug
println!("{:#?}", p); // Pretty Debug
println!("{}", p); // Display
}Clone và Copy
rust
#[derive(Clone, Debug)]
struct Person {
name: String,
age: u8,
}
#[derive(Copy, Clone, Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let person1 = Person {
name: String::from("Alice"),
age: 30,
};
let person2 = person1.clone();
println!("{:?}", person1); // Still valid
println!("{:?}", person2);
let p1 = Point { x: 1, y: 2 };
let p2 = p1; // Copy
println!("{:?}", p1); // Still valid
println!("{:?}", p2);
}Iterator
rust
struct Counter {
count: u32,
}
impl Counter {
fn new() -> Counter {
Counter { count: 0 }
}
}
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
if self.count < 5 {
self.count += 1;
Some(self.count)
} else {
None
}
}
}
fn main() {
let mut counter = Counter::new();
while let Some(val) = counter.next() {
println!("{}", val);
}
// Or use for loop
for val in Counter::new() {
println!("{}", val);
}
// Chain iterator methods
let sum: u32 = Counter::new()
.zip(Counter::new().skip(1))
.map(|(a, b)| a * b)
.filter(|x| x % 3 == 0)
.sum();
println!("Sum: {}", sum);
}From và Into
rust
#[derive(Debug)]
struct Number {
value: i32,
}
impl From<i32> for Number {
fn from(item: i32) -> Self {
Number { value: item }
}
}
fn main() {
let num = Number::from(30);
println!("{:?}", num);
// Into automatically available
let num: Number = 40.into();
println!("{:?}", num);
}String conversions:
rust
fn main() {
let my_str = "hello";
let my_string = String::from(my_str);
let my_string2: String = my_str.into();
println!("{}", my_string);
println!("{}", my_string2);
}Drop
rust
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
}
}
fn main() {
let c = CustomSmartPointer {
data: String::from("my stuff"),
};
let d = CustomSmartPointer {
data: String::from("other stuff"),
};
println!("CustomSmartPointers created.");
// Explicit drop
drop(c);
println!("CustomSmartPointer dropped before end of main.");
// d dropped automatically at end of scope
}🧪 BÀI TẬP LỚN CUỐI PHẦN: Generic Data Processing Library
Mô tả bài toán
Xây dựng thư viện xử lý dữ liệu generic với:
- Generic data structures (List, Stack, Queue)
- Data transformations (Map, Filter, Reduce)
- Statistical operations
- Custom iterators
- Trait-based polymorphism
- Type-safe operations
Yêu cầu
1. Generic data structures:
rust
pub trait Collection<T> {
fn add(&mut self, item: T);
fn remove(&mut self) -> Option<T>;
fn is_empty(&self) -> bool;
fn size(&self) -> usize;
}
pub struct Stack<T> {
items: Vec<T>,
}
pub struct Queue<T> {
items: Vec<T>,
}
pub struct List<T> {
items: Vec<T>,
}2. Data transformation traits:
rust
pub trait Transformable<T> {
fn map<F, U>(self, f: F) -> Vec<U>
where
F: FnMut(T) -> U;
fn filter<F>(self, predicate: F) -> Vec<T>
where
F: FnMut(&T) -> bool;
fn reduce<F>(self, initial: T, f: F) -> T
where
F: FnMut(T, T) -> T;
}
impl<T> Transformable<T> for Vec<T> {
// Implement methods
}3. Statistical operations:
rust
pub trait Statistics {
fn mean(&self) -> f64;
fn median(&self) -> f64;
fn mode(&self) -> Vec<Self>
where
Self: Sized;
fn std_dev(&self) -> f64;
}
impl Statistics for Vec<f64> {
// Implement for numeric types
}4. Custom iterator:
rust
pub struct RangeIterator<T> {
current: T,
end: T,
step: T,
}
impl<T> Iterator for RangeIterator<T>
where
T: Copy + PartialOrd + std::ops::Add<Output = T>,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
// Implement
}
}Template code:
rust
use std::fmt::{Debug, Display};
use std::ops::{Add, Div};
use std::collections::HashMap;
// ============ Collection Trait ============
pub trait Collection<T> {
fn add(&mut self, item: T);
fn remove(&mut self) -> Option<T>;
fn is_empty(&self) -> bool;
fn size(&self) -> usize;
fn clear(&mut self);
}
// ============ Stack ============
#[derive(Debug)]
pub struct Stack<T> {
items: Vec<T>,
}
impl<T> Stack<T> {
pub fn new() -> Self {
Stack { items: Vec::new() }
}
pub fn peek(&self) -> Option<&T> {
self.items.last()
}
}
impl<T> Collection<T> for Stack<T> {
fn add(&mut self, item: T) {
self.items.push(item);
}
fn remove(&mut self) -> Option<T> {
self.items.pop()
}
fn is_empty(&self) -> bool {
self.items.is_empty()
}
fn size(&self) -> usize {
self.items.len()
}
fn clear(&mut self) {
self.items.clear();
}
}
// ============ Queue ============
#[derive(Debug)]
pub struct Queue<T> {
items: Vec<T>,
}
impl<T> Queue<T> {
pub fn new() -> Self {
Queue { items: Vec::new() }
}
pub fn peek(&self) -> Option<&T> {
self.items.first()
}
}
impl<T> Collection<T> for Queue<T> {
fn add(&mut self, item: T) {
self.items.push(item);
}
fn remove(&mut self) -> Option<T> {
if self.items.is_empty() {
None
} else {
Some(self.items.remove(0))
}
}
fn is_empty(&self) -> bool {
self.items.is_empty()
}
fn size(&self) -> usize {
self.items.len()
}
fn clear(&mut self) {
self.items.clear();
}
}
// ============ Transformable Trait ============
pub trait Transformable<T> {
fn transform_map<F, U>(self, f: F) -> Vec<U>
where
F: FnMut(T) -> U;
fn transform_filter<F>(self, predicate: F) -> Vec<T>
where
F: FnMut(&T) -> bool;
}
impl<T> Transformable<T> for Vec<T> {
fn transform_map<F, U>(self, mut f: F) -> Vec<U>
where
F: FnMut(T) -> U,
{
let mut result = Vec::new();
for item in self {
result.push(f(item));
}
result
}
fn transform_filter<F>(self, mut predicate: F) -> Vec<T>
where
F: FnMut(&T) -> bool,
{
let mut result = Vec::new();
for item in self {
if predicate(&item) {
result.push(item);
}
}
result
}
}
// ============ Statistics Trait ============
pub trait Statistics {
fn mean(&self) -> f64;
fn median(&self) -> f64;
fn std_dev(&self) -> f64;
fn min(&self) -> f64;
fn max(&self) -> f64;
}
impl Statistics for Vec<f64> {
fn mean(&self) -> f64 {
if self.is_empty() {
return 0.0;
}
self.iter().sum::<f64>() / self.len() as f64
}
fn median(&self) -> f64 {
if self.is_empty() {
return 0.0;
}
let mut sorted = self.clone();
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap());
let mid = sorted.len() / 2;
if sorted.len() % 2 == 0 {
(sorted[mid - 1] + sorted[mid]) / 2.0
} else {
sorted[mid]
}
}
fn std_dev(&self) -> f64 {
if self.is_empty() {
return 0.0;
}
let mean = self.mean();
let variance = self.iter()
.map(|x| (x - mean).powi(2))
.sum::<f64>() / self.len() as f64;
variance.sqrt()
}
fn min(&self) -> f64 {
self.iter()
.copied()
.min_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap_or(0.0)
}
fn max(&self) -> f64 {
self.iter()
.copied()
.max_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap_or(0.0)
}
}
// ============ Custom Iterator ============
pub struct RangeIterator<T> {
current: T,
end: T,
step: T,
}
impl<T> RangeIterator<T> {
pub fn new(start: T, end: T, step: T) -> Self {
RangeIterator {
current: start,
end,
step,
}
}
}
impl<T> Iterator for RangeIterator<T>
where
T: Copy + PartialOrd + Add<Output = T>,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.current < self.end {
let result = self.current;
self.current = self.current + self.step;
Some(result)
} else {
None
}
}
}
// ============ Pair struct for combining types ============
#[derive(Debug, Clone)]
pub struct Pair<T, U> {
pub first: T,
pub second: U,
}
impl<T, U> Pair<T, U> {
pub fn new(first: T, second: U) -> Self {
Pair { first, second }
}
pub fn swap(self) -> Pair<U, T> {
Pair {
first: self.second,
second: self.first,
}
}
}
impl<T: Display, U: Display> Display for Pair<T, U> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "({}, {})", self.first, self.second)
}
}
// ============ Tests ============
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stack() {
let mut stack = Stack::new();
stack.add(1);
stack.add(2);
stack.add(3);
assert_eq!(stack.size(), 3);
assert_eq!(stack.remove(), Some(3));
assert_eq!(stack.remove(), Some(2));
}
#[test]
fn test_queue() {
let mut queue = Queue::new();
queue.add(1);
queue.add(2);
queue.add(3);
assert_eq!(queue.remove(), Some(1));
assert_eq!(queue.remove(), Some(2));
}
#[test]
fn test_transformable() {
let numbers = vec![1, 2, 3, 4, 5];
let doubled = numbers.clone().transform_map(|x| x * 2);
assert_eq!(doubled, vec![2, 4, 6, 8, 10]);
let evens = numbers.transform_filter(|x| x % 2 == 0);
assert_eq!(evens, vec![2, 4]);
}
#[test]
fn test_statistics() {
let numbers = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_eq!(numbers.mean(), 3.0);
assert_eq!(numbers.median(), 3.0);
assert_eq!(numbers.min(), 1.0);
assert_eq!(numbers.max(), 5.0);
}
#[test]
fn test_range_iterator() {
let range = RangeIterator::new(0, 10, 2);
let values: Vec<i32> = range.collect();
assert_eq!(values, vec![0, 2, 4, 6, 8]);
}
}
// ============ Example Usage ============
fn main() {
println!("=== Stack Example ===");
let mut stack = Stack::new();
stack.add(1);
stack.add(2);
stack.add(3);
println!("Stack size: {}", stack.size());
while let Some(item) = stack.remove() {
println!("Popped: {}", item);
}
println!("\n=== Queue Example ===");
let mut queue = Queue::new();
queue.add("first");
queue.add("second");
queue.add("third");
while let Some(item) = queue.remove() {
println!("Dequeued: {}", item);
}
println!("\n=== Transformations ===");
let numbers = vec![1, 2, 3, 4, 5];
let squared = numbers.clone().transform_map(|x| x * x);
println!("Squared: {:?}", squared);
let evens = numbers.clone().transform_filter(|x| x % 2 == 0);
println!("Evens: {:?}", evens);
println!("\n=== Statistics ===");
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0];
println!("Mean: {:.2}", data.mean());
println!("Median: {:.2}", data.median());
println!("Std Dev: {:.2}", data.std_dev());
println!("Min: {:.2}", data.min());
println!("Max: {:.2}", data.max());
println!("\n=== Custom Iterator ===");
let range = RangeIterator::new(0.0, 5.0, 0.5);
for value in range {
println!("{:.1}", value);
}
println!("\n=== Pair Example ===");
let pair = Pair::new("Hello", 42);
println!("Pair: {}", pair);
let swapped = pair.clone().swap();
println!("Swapped: {} {}", swapped.first, swapped.second);
}Yêu cầu mở rộng:
Additional data structures:
- Binary tree
- Hash table
- Linked list
- Priority queue
More transformations:
- FlatMap
- Take/Skip
- Zip
- Partition
- Group by
Advanced statistics:
- Percentiles
- Correlation
- Regression
- Moving averages
Parallel processing:
- Parallel map/filter
- Thread pool
- Work stealing
Serialization:
- JSON support
- CSV export
- Binary format
Visualization:
- ASCII charts
- Data export for plotting
- Summary reports
