메소드(method)는 함수와 유사합니다. 하지만 메소드는 함수와 달리 구조체의 내용 안(혹은 열거형이나 트레잇 객체 안)에 정의됩니다. 첫 번째 파라미터가 언제나 self 인데 이는 메소드가 호출되고 있는 구조체의 인스턴스를 나타냅니다.
chapter5-2의 코드를 수정해봅시다. Rectangle
인스턴스를 파라미터로 가지고 있는 area
함수를 변경하여 Rectangle
구조체 위에서 정의된 area
메소드를 만들어봅시다.
#[derive(Debug)]
struct Rectangle {
length: u32,
width: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.length * self.width
}
}
fn main() {
let rect1 = Rectangle { length: 50, width: 30 };
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
}
Rectangle
의 내용 안에 함수를 정의하기 위해 impl
블록을 시작합니다. area
함수를 impl
중괄호 안으로 옮기고 시그니처 본체 내의 모든 곳에 있는 첫 번째 파라미터를 self
로 변경합니다. main
함수에서는 Rectangle
인스턴스 상의 area
메소드를 호풀하기 위해 메소드 문법 (method syntax) 를 이용할 수 있습니다.
이전 코드에서 &Rectangle
을 사용한것과 같이 &self
를 사용했습니다. 소유권을 가져오는 것을 원하지 않고 데이터만 읽어오기 위함입니다.
이번에는 Rectangle
의 인스턴스가 다른 Rectangle
인스턴스를 가져와서 두 번째 Rectangle
이 self
내에 완전히 들어갈 수 있다면 true
를 반환하고 그렇지 않으면 false
를 반환하도록 작성해봅시다.
impl Rectangle {
fn area(&self) -> u32 {
self.length * self.width
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.length > other.length && self.width > other.width
}
}
fn main() {
let rect1 = Rectangle { length: 50, width: 30 };
let rect2 = Rectangle { length: 40, width: 10 };
let rect3 = Rectangle { length: 45, width: 60 };
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}
결과:
Can rect1 hold rect2? true
Can rect1 hold rect3? false
rect1.can_hold(&rect2)
는 &rect2
를 넘기고 있는데, 이는 Rectangle
의 인스턴스인 rect2
의 불변성 빌림입니다. 우리가 rect2
를 그냥 읽기만 하길 원하기 때문입니다. main
이 rect2
의 소유권을 유지하여 can_hold
메소드 호출 후에도 다시 사용할 수 있습니다.
self
파라미터를 갖지 않는 함수도 impl
내에 정의 가능합니다. 이를 연관 함수(associated functions) 라고 부릅니다. 이 함수가 해당 구조체와 연관되어 있기 때문입니다. 함께 동작할 구조체의 인스턴스를 가지고 있지 않기 때문에 메소드가 아니라 여전히 함수입니다.
연관 함수는 아래의 예시처럼 새로운 구조체의 인스턴스를 반환해주는 생성자로 주로 사용됩니다.
impl Rectangle {
fn square(size: u32) -> Rectangle {
Rectangle { length: size, width: size }
}
}
연관 함수를 호출하지 위해서는 let sq = Rectangle::square(3);
처럼, 구조체 이름과 함께 ::
문법을 이용합니다.