연관 타입은 트레이트 내에서 타입 플레이스홀더를 정의합니다. 제네릭보다 간결하고 사용하기 쉽습니다.
// 제네릭 방식 (덜 선호)
trait Container<T> {
fn add(&mut self, item: T);
}
// 연관 타입 방식 (더 선호)
trait Container {
type Item;
fn add(&mut self, item: Self::Item);
}
struct Stack<T> {
items: Vec<T>,
}
impl<T> Container for Stack<T> {
type Item = T;
fn add(&mut self, item: T) {
self.items.push(item);
}
}
fn main() {
let mut stack = Stack { items: vec![] };
stack.add(42);
stack.add(100);
}
// 표준 라이브러리의 Iterator
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
// 커스텀 이터레이터 구현
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 counter = Counter::new();
for num in counter {
println!("{}", num); // 1, 2, 3, 4, 5
}
}
트레이트 객체를 사용하면 다른 타입의 값을 동일하게 처리할 수 있습니다.
trait Draw {
fn draw(&self);
}
struct Circle {
radius: f64,
}
struct Rectangle {
width: f64,
height: f64,
}
impl Draw for Circle {
fn draw(&self) {
println!("Drawing circle with radius {}", self.radius);
}
}
impl Draw for Rectangle {
fn draw(&self) {
println!("Drawing rectangle {}x{}", self.width, self.height);
}
}
// 정적 디스패치 (컴파일 타임, 더 빠름)
fn draw_static<T: Draw>(shape: &T) {
shape.draw();
}
// 동적 디스패치 (런타임, 더 유연함)
fn draw_dynamic(shape: &dyn Draw) {
shape.draw();
}
fn main() {
let circle = Circle { radius: 5.0 };
let rect = Rectangle { width: 10.0, height: 20.0 };
draw_static(&circle);
draw_static(&rect);
draw_dynamic(&circle);
draw_dynamic(&rect);
// 트레이트 객체 벡터
let shapes: Vec<Box<dyn Draw>> = vec![
Box::new(Circle { radius: 3.0 }),
Box::new(Rectangle { width: 5.0, height: 7.0 }),
];
for shape in shapes.iter() {
shape.draw();
}
}
// 객체 안전한 트레이트
trait Animal {
fn make_sound(&self);
}
// 객체 안전하지 않은 트레이트
trait Cloneable {
fn clone(&self) -> Self; // Self 반환 = 객체 안전하지 않음
}
// 수정된 객체 안전한 버전
trait CloneBox {
fn clone_box(&self) -> Box<dyn CloneBox>;
}
impl<T: Clone + 'static> CloneBox for T {
fn clone_box(&self) -> Box<dyn CloneBox> {
Box::new(self.clone())
}
}
use std::fmt::Display;
// 복잡한 제네릭 함수
fn complex_function<T, U>(t: &T, u: &U) -> i32
where
T: Display + Clone,
U: Clone + Debug,
{
println!("t: {}", t);
println!("u: {:?}", u);
42
}
// 연관 타입 바운드
fn print_items<C>(container: C)
where
C: Iterator,
C::Item: Display,
{
for item in container {
println!("{}", item);
}
}
use std::fmt;
// Display를 구현하려면 반드시 Debug도 구현해야 함
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();
}
use std::ops::Add;
#[derive(Debug, Copy, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
// Add 트레이트의 디폴트 타입 매개변수
impl Add for Point {
type Output = Point;
fn add(self, other: Point) -> Point {
Point {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
// 다른 타입과의 덧셈
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() {
assert_eq!(
Point { x: 1, y: 0 } + Point { x: 2, y: 3 },
Point { x: 3, y: 3 }
);
let mm = Millimeters(1000);
let m = Meters(1);
let total = mm + m;
println!("{}mm", total.0);
}
trait Pilot {
fn fly(&self);
}
trait Wizard {
fn fly(&self);
}
struct Human;
impl Pilot for Human {
fn fly(&self) {
println!("This is your captain speaking.");
}
}
impl Wizard for Human {
fn fly(&self) {
println!("Up!");
}
}
impl Human {
fn fly(&self) {
println!("*waving arms furiously*");
}
}
fn main() {
let person = Human;
person.fly(); // Human::fly 호출
// 명시적으로 호출
Pilot::fly(&person);
Wizard::fly(&person);
Human::fly(&person);
// 연관 함수의 경우
trait Animal {
fn baby_name() -> String;
}
struct Dog;
impl Dog {
fn baby_name() -> String {
String::from("Spot")
}
}
impl Animal for Dog {
fn baby_name() -> String {
String::from("puppy")
}
}
println!("A baby dog is called a {}", Dog::baby_name());
// 완전 정규화 구문
println!("A baby dog is called a {}", <Dog as Animal>::baby_name());
}
use std::collections::HashMap;
// 플러그인 트레이트
trait Plugin: Send + Sync {
fn name(&self) -> &str;
fn execute(&self, args: &[&str]) -> Result<String, String>;
fn help(&self) -> &str;
}
// 구체적인 플러그인들
struct EchoPlugin;
impl Plugin for EchoPlugin {
fn name(&self) -> &str {
"echo"
}
fn execute(&self, args: &[&str]) -> Result<String, String> {
Ok(args.join(" "))
}
fn help(&self) -> &str {
"Echo back the input arguments"
}
}
struct UpperPlugin;
impl Plugin for UpperPlugin {
fn name(&self) -> &str {
"upper"
}
fn execute(&self, args: &[&str]) -> Result<String, String> {
Ok(args.join(" ").to_uppercase())
}
fn help(&self) -> &str {
"Convert input to uppercase"
}
}
// 플러그인 매니저
struct PluginManager {
plugins: HashMap<String, Box<dyn Plugin>>,
}
impl PluginManager {
fn new() -> Self {
PluginManager {
plugins: HashMap::new(),
}
}
fn register(&mut self, plugin: Box<dyn Plugin>) {
let name = plugin.name().to_string();
self.plugins.insert(name, plugin);
}
fn execute(&self, name: &str, args: &[&str]) -> Result<String, String> {
self.plugins
.get(name)
.ok_or_else(|| format!("Plugin '{}' not found", name))?
.execute(args)
}
fn list_plugins(&self) {
println!("Available plugins:");
for (name, plugin) in &self.plugins {
println!(" {}: {}", name, plugin.help());
}
}
}
fn main() {
let mut manager = PluginManager::new();
manager.register(Box::new(EchoPlugin));
manager.register(Box::new(UpperPlugin));
manager.list_plugins();
match manager.execute("echo", &["Hello", "World"]) {
Ok(result) => println!("Result: {}", result),
Err(e) => println!("Error: {}", e),
}
match manager.execute("upper", &["hello", "rust"]) {
Ok(result) => println!("Result: {}", result),
Err(e) => println!("Error: {}", e),
}
}