제네릭 메서드
Coddy Rust 여정의 Object Oriented Programming 섹션에 포함된 레슨 — 61개 중 25번째.
제네릭 구조체는 어떤 타입이든 가질 수 있지만, 그 데이터와 상호작용하는 메서드가 없다면 그리 유용하지 않습니다. 제네릭 구조체에 대한 메서드를 정의하려면 impl 블록에서 특별한 구문이 필요합니다.
핵심은 impl 자체에 제네릭 매개변수를 선언하는 것입니다:
struct Wrapper<T> {
value: T,
}
impl<T> Wrapper<T> {
fn get(&self) -> &T {
&self.value
}
}
Wrapper<T> 앞에 있는 impl<T>를 주목하세요. 이것은 T가 전체 구현 블록에 대한 제네릭 타입 매개변수임을 Rust에게 알려줍니다. 이 선언이 없으면 Rust는 T라는 이름의 구체적인 타입을 찾으려 시도하다가 실패하게 됩니다.
get 메서드는 래퍼가 보유한 모든 유형에 대한 참조인 &T를 반환합니다. 이는 T가 정수이든, 문자열이든, 다른 어떤 유형이든 상관없이 작동합니다:
let num_wrapper = Wrapper { value: 100 };
let text_wrapper = Wrapper { value: "Rust" };
println!("{}", num_wrapper.get()); // 100
println!("{}", text_wrapper.get()); // Rust
동일한 메서드 정의가 두 경우 모두에 작동합니다. 제네릭 T가 컴파일 타임에 각 구체적인 타입에 맞춰지기 때문입니다.
챌린지
쉬움메서드를 사용하여 제네릭 컨테이너를 확장해 봅시다! 어떤 타입이든 담을 수 있고 그 내용물과 상호작용하는 메서드를 제공하는 Box 구조체(Rust의 표준 Box와 혼동하지 마세요)를 만들 것입니다.
코드는 두 개의 파일로 구성됩니다:
mybox.rs:T타입의 비공개 필드contents를 가진MyBox<T>라는 공개 제네릭 구조체를 정의합니다. 이 구조체에 대해 다음 메서드들을 구현하세요:- 주어진 값으로 새로운
MyBox를 생성하는new연관 함수 - 내용물에 대한 참조를 반환하는
peek메서드 (&self사용) - 새로운 값을 받아 현재 내용물을 교체하는
replace메서드 (&mut self사용)
- 주어진 값으로 새로운
main.rs: 모듈을 가져와서 서로 다른 타입으로 작동하는 제네릭 메서드를 시연합니다. 박스를 생성하고, 내용물을 훔쳐보고(peek), 값을 교체하여 메서드가 실제로 작동하는 모습을 보여줍니다.
제네릭 구조체에 메서드를 구현할 때의 핵심 문법을 기억하세요: MyBox<T> 앞에 impl<T>를 작성하여 Rust에게 T가 전체 구현 블록에 대한 제네릭 매개변수임을 알려야 합니다.
메인 파일에서 다음 과정을 통해 MyBox를 시연하세요:
- 정수(첫 번째 입력,
i32로 파싱됨)로 박스를 생성합니다. - 내용물을 훔쳐보고(peek) 값을 출력합니다.
- 내용물을 새로운 정수(두 번째 입력,
i32로 파싱됨)로 교체합니다. - 다시 훔쳐보며(peek) 업데이트된 값을 보여줍니다.
- 문자열(세 번째 입력)로 두 번째 박스를 생성합니다.
- 문자열 박스의 내용물을 훔쳐봅니다(peek).
출력은 다음 형식을 따라야 합니다:
Integer box contains: {value}
After replace: {value}
String box contains: {value}예를 들어, 입력값이 10, 25, Rust인 경우:
Integer box contains: 10
After replace: 25
String box contains: Rust초기 정수, 교체할 정수, 그리고 문자열 값까지 총 세 개의 입력을 받게 됩니다.
치트 시트
제네릭 구조체에 대한 메서드를 정의하려면, impl 블록에 제네릭 매개변수를 선언하세요:
struct Wrapper<T> {
value: T,
}
impl<T> Wrapper<T> {
fn get(&self) -> &T {
&self.value
}
}
impl<T> 구문은 Rust에게 T가 전체 구현 블록에 대한 제네릭 타입 매개변수임을 알려줍니다. 이것이 없으면 Rust는 T라는 이름의 구체적인 타입을 찾으려고 할 것입니다.
메서드는 제네릭 타입에 대한 참조(&T)를 반환할 수 있으며, 이는 모든 구체적인 타입에 대해 작동합니다:
let num_wrapper = Wrapper { value: 100 };
let text_wrapper = Wrapper { value: "Rust" };
println!("{}", num_wrapper.get()); // 100
println!("{}", text_wrapper.get()); // Rust
직접 해보기
mod mybox;
use mybox::MyBox;
fn main() {
// 입력 읽기
let mut input1 = String::new();
std::io::stdin().read_line(&mut input1).expect("Failed to read line");
let initial_int: i32 = input1.trim().parse().expect("Invalid integer");
let mut input2 = String::new();
std::io::stdin().read_line(&mut input2).expect("Failed to read line");
let replacement_int: i32 = input2.trim().parse().expect("Invalid integer");
let mut input3 = String::new();
std::io::stdin().read_line(&mut input3).expect("Failed to read line");
let string_value = input3.trim().to_string();
// TODO: 초기 정수값으로 MyBox 생성
// TODO: 내용을 확인하고 출력: "Integer box contains: {value}"
// TODO: 내용을 교체용 정수값으로 변경
// TODO: 다시 확인하고 출력: "After replace: {value}"
// TODO: 문자열 값으로 두 번째 MyBox 생성
// TODO: 문자열 박스를 확인하고 출력: "String box contains: {value}"
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.