트레이트 객체 순회하기
Coddy Rust 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 61개 중 47번째.
이제 Vec<Box<dyn Trait>>에 다양한 타입을 저장할 수 있으므로, 다음 단계는 해당 collection을 처리하는 것입니다. 트레이트 객체를 순회하는 것은 다른 벡터를 순회하는 것과 똑같이 작동합니다. 각 요소를 순회하며 트레이트에 정의된 메서드를 호출합니다.
trait Shape {
fn area(&self) -> f64;
}
struct Circle { radius: f64 }
struct Rectangle { width: f64, height: f64 }
impl Shape for Circle {
fn area(&self) -> f64 { 3.14159 * self.radius * self.radius }
}
impl Shape for Rectangle {
fn area(&self) -> f64 { self.width * self.height }
}
fn main() {
let shapes: Vec<Box<dyn Shape>> = vec![
Box::new(Circle { radius: 2.0 }),
Box::new(Rectangle { width: 3.0, height: 4.0 }),
Box::new(Circle { radius: 1.0 }),
];
for shape in &shapes {
println!("Area: {}", shape.area());
}
}
for shape in &shapes로 Iterate할 때 각 shape은 Box<dyn Shape>에 대한 참조입니다. Shape 트레이트의 어떤 method이든 직접 호출할 수 있습니다. Rust는 동적 디스패치를 자동으로 처리하여 각 구체 타입에 맞는 구현을 호출합니다.
이 패턴은 결과를 집계하는 데 강력합니다. 모든 area를 합산하거나, 크기에 따라 shapes를 필터링하거나, collection을 변환할 수 있습니다. 핵심은 items가 trait object collection에 들어가면 기본 타입과 관계없이 trait의 인터페이스를 통해서만 상호 작용한다는 것입니다.
챌린지
쉬움서로 다른 유형의 항목을 담을 수 있는 쇼핑 카트용 가격 계산기를 만들어 봅시다! 서로 다른 가격 책정 로직을 가진 제품과 서비스를 함께 저장하고 처리하여 총 가격을 계산하는 시스템을 만들게 됩니다.
코드를 두 개의 파일로 구성합니다:
items.rs:&self를 받고f64를 반환하는price메서드를 포함한 publicPriceabletrait을 정의합니다. 그런 다음 두 개의 public struct를 만듭니다:Product: publicname(String) 및cost(f64) 필드를 가집니다. 해당price는 간단히cost를 반환해야 합니다.Service: publicname(String),hourly_rate(f64),hours(f64) 필드를 가집니다. 해당price는hourly_rate * hours를 반환해야 합니다.
main.rs: items 모듈을 가져오고Vec<Box<dyn Priceable>>유형의 벡터를 만듭니다. 입력값을 사용하여 Product 하나와 Service 하나를 만들고, 이를 벡터에 추가한 다음 컬렉션을 순회하며 모든 항목의 총 가격을 계산하고 출력합니다.
여기서 trait 객체의 강력한 기능이 빛을 발합니다. 순회 코드는 Product를 처리하는지 Service를 처리하는지 알 필요가 없습니다. 각 항목에 대해 간단히 price()를 호출하면 Rust가 올바른 구현으로 자동 디스패치합니다.
출력에는 소수점 이하 두 자리까지 표시된 총 가격이 나타나야 합니다:
Total: ${total}예를 들어 입력값이 Laptop, 999.99, Consulting, 150.0, 3.0인 경우:
Total: $1449.99입력값이 Book, 29.99, Tutoring, 50.0, 2.0인 경우:
Total: $129.99다섯 개의 입력을 받습니다: 제품 이름, 제품 비용, 서비스 이름, 시간당 요금, 근무 시간(f64로 숫자 값을 파싱합니다).
직접 해보기
mod items;
use items::{Priceable, Product, Service};
fn main() {
// 입력 읽기
let mut product_name = String::new();
std::io::stdin().read_line(&mut product_name).expect("Failed to read line");
let product_name = product_name.trim().to_string();
let mut product_cost = String::new();
std::io::stdin().read_line(&mut product_cost).expect("Failed to read line");
let product_cost: f64 = product_cost.trim().parse().expect("Failed to parse");
let mut service_name = String::new();
std::io::stdin().read_line(&mut service_name).expect("Failed to read line");
let service_name = service_name.trim().to_string();
let mut hourly_rate = String::new();
std::io::stdin().read_line(&mut hourly_rate).expect("Failed to read line");
let hourly_rate: f64 = hourly_rate.trim().parse().expect("Failed to parse");
let mut hours = String::new();
std::io::stdin().read_line(&mut hours).expect("Failed to read line");
let hours: f64 = hours.trim().parse().expect("Failed to parse");
// TODO: product_name과 product_cost를 사용하여 Product 생성
// TODO: service_name, hourly_rate, hours를 사용하여 Service 생성
// TODO: Vec<Box<dyn Priceable>>를 생성하고 두 항목을 추가
// TODO: 벡터를 순회하며 총 가격 계산
// TODO: 소수 첫째 자리까지 총액 출력
// 형식: Total: ${total:.1}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
직접 연습해 보세요: 온라인 Rust 컴파일러