🎭 Traits: Hành Vi Chung
🎯 Mục Tiêu Bài Học
Sau khi hoàn thành bài học này, bạn sẽ:
- ✅ Hiểu được trait là gì và khi nào dùng
- ✅ Định nghĩa traits
- ✅ Implement traits cho structs
- ✅ Sử dụng trait bounds
- ✅ Default implementations
- ✅ Common traits trong Rust
🤔 Trait Là Gì?
Ẩn Dụ Cuộc Sống: Giấy Phép Lái Xe
Trait giống như giấy phép/chứng chỉ:
🚗 Giấy Phép Lái Xe:
- Chứng nhận bạn có khả năng lái xe
- Không quan tâm bạn lái xe gì (ô tô, xe máy, xe tải)
- Đảm bảo bạn biết cách lái
🎭 Trait Trong Rust:
- Định nghĩa hành vi chung
- Nhiều kiểu khác nhau có thể implement
- Đảm bảo kiểu đó có các methods
Ví Dụ Đơn Giản
// Định nghĩa trait
trait Speak {
fn speak(&self);
}
// Implement cho Dog
struct Dog;
impl Speak for Dog {
fn speak(&self) {
println!("Gâu gâu!");
}
}
// Implement cho Cat
struct Cat;
impl Speak for Cat {
fn speak(&self) {
println!("Meo meo!");
}
}
fn main() {
let dog = Dog;
let cat = Cat;
dog.speak();
cat.speak();
}
Đầu ra:
Gâu gâu!
Meo meo!
📝 Định Nghĩa Traits
Cú Pháp Cơ Bản
trait Summary {
fn summarize(&self) -> String;
}
struct Article {
title: String,
content: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
format!("{}: {}", self.title, self.content)
}
}
fn main() {
let article = Article {
title: String::from("Rust"),
content: String::from("Amazing language"),
};
println!("{}", article.summarize());
}
Multiple Methods
trait Shape {
fn area(&self) -> f64;
fn perimeter(&self) -> f64;
}
struct Rectangle {
width: f64,
height: f64,
}
impl Shape for Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
fn perimeter(&self) -> f64 {
2.0 * (self.width + self.height)
}
}
struct Circle {
radius: f64,
}
impl Shape for Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * self.radius * self.radius
}
fn perimeter(&self) -> f64 {
2.0 * std::f64::consts::PI * self.radius
}
}
fn main() {
let rect = Rectangle {
width: 10.0,
height: 5.0,
};
println!("Rectangle area: {}", rect.area());
let circle = Circle { radius: 3.0 };
println!("Circle perimeter: {:.2}", circle.perimeter());
}
🎨 Default Implementations
trait Greet {
fn greet(&self) -> String {
String::from("Hello!") // Default
}
fn greet_loudly(&self) -> String {
format!("{}!!!", self.greet()) // Dùng default greet
}
}
struct Person {
name: String,
}
// Dùng default
impl Greet for Person {}
struct Robot {
id: u32,
}
// Override default
impl Greet for Robot {
fn greet(&self) -> String {
format!("Beep boop! Robot {}", self.id)
}
}
fn main() {
let person = Person {
name: String::from("Alice"),
};
println!("{}", person.greet());
println!("{}", person.greet_loudly());
let robot = Robot { id: 42 };
println!("{}", robot.greet());
println!("{}", robot.greet_loudly());
}
Đầu ra:
Hello!
Hello!!!
Beep boop! Robot 42
Beep boop! Robot 42!!!
🔗 Trait Bounds
Function Parameters
trait Summary {
fn summarize(&self) -> String;
}
// Cách 1: impl Trait
fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
// Cách 2: Trait bound
fn notify2<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
// Cách 3: where clause
fn notify3<T>(item: &T)
where
T: Summary,
{
println!("Breaking news! {}", item.summarize());
}
Multiple Bounds
use std::fmt::Display;
fn print_info<T: Display + Clone>(value: T) {
println!("Value: {}", value);
let cloned = value.clone();
println!("Cloned: {}", cloned);
}
fn main() {
print_info(42);
print_info("Hello");
}
Return Types
trait Animal {
fn sound(&self) -> String;
}
struct Dog;
impl Animal for Dog {
fn sound(&self) -> String {
String::from("Woof!")
}
}
fn create_animal() -> impl Animal {
Dog // Return concrete type
}
fn main() {
let animal = create_animal();
println!("{}", animal.sound());
}