0%

Rust 메서드 문법

러스트 메서드 문법에 대해 공부한 내용을 정리합니다.

메서드

메서드는 간단하게 말하면 특정 구조체에서 사용할 수 있는 함수라고 볼 수 있다. 메서드는 함수와 동일하게 fn으로 정의하고, 매개변수와 결과값도 지정한다. 다만 메서드는 일반 함수를 정의할 때와 달리 impl 블록 안에 함수들을 정의해야 한다는 점과 첫 번쨰 매개변수로 &self를 넣어야 한다는 점이 있다. 메서드 예시 코드를 보면서 좀 더 알아보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct Rectangle {
width: i32,
height: i32
}

impl Rectangle {
fn area(&self) -> i32 {
self.width * self.height
}
}

fn main() {
let rec1 = Rectangle {width:30, height:50};
println!("사각형의 면적은 {}입니다.", rec1.area());
}

위 코드는 사각형의 면적을 구하는 프로그램을 작성한 것이다. 메서드를 정의하기 위해서는 impl 뒤에 구조체 이름을 붙이면 된다. 위 구조는 impl 컨텍스트 내부에 정의되는 모든 함수들을 Rectangle의 메서드로 사용하겠다는 의미를 가진다. 정의한 메서드를 사용하는 방식은 해당 구조체 인스턴스 다음에 마침표, 메서드의 이름, 괄호 순서로 작성하면 된다.
area()라는 메서드의 매개변수는 Rectangle이 아닌 self를 사용한다. 이 또한 이미 area()Rectangle의 컨텍스트 내부에 정의됨으로서 해당 구조체의 메서드라는 것을 러스트가 알고 있기애 굳이 다시 데이터 타입을 알려줄 필요가 없다는 것을 의미한다.
위 코드는 area()Rectangle 필드 값을 참조만 하면 되기에 &self을 사용하였다. 상황에 따라서 self 또한 가변변수로 대여하거나 소유권을 가져오는 등의 설정도 가능하다.

많은 매개변수를 가진 메서드

이제 메서드에 대해 기본적인 구조는 배웠다. 이를 바탕으로 아래의 문제를 풀어보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Q: 아래의 코드에서 compare()을 작성하여 프로그램을 완성해보자.

struct Rectangle {
width: i32,
height: i32
}

fn main() {
let rec1 = Rectangle {width: 40, height:30};
let rec2 = Rectangle {width: 50, height:30};
let rec3 = Rectangle {width: 20, height:30};

println!("rec1은 rec2를 가릴 수 있나요? {}", rec1.compare(&rec2));
println!("rec1는 rec3를 가릴 수 있나요? {}", rec1.compare(&rec3));
}

위 문제를 해결하기 위해서는 rec1.compare(&rec2)에 해당하는 함수가 필요하다. compare()은 예측했겠지만 Rectangle의 메서드이다. 제대로 작동하는 메서드 코드를 작성했다면 결과는 아래과 같다.

1
2
rec1은 rec2를 가릴 수 있나요? false
rec1는 rec3를 가릴 수 있나요? true

위 문제는 rec1이 나머지 사각형을 가릴 수 있는지 여부를 판단하는 것이었다. 즉, 가로, 세로 모든 면적이 다른 사각형보다 길거나 같아야지만 가릴 수 있다. 따라서 rec1은 rec2보다 width가 짧기에 가릴 수 없으며 정답은 false 이며, rec3와 비교했을 때 rec1 모든 변의 길이가 같거나 더 길기에 정답은 true이다. 위 문제를 해결하는 코드는 다양하겠지만 아래의 코드는 위 설명을 그대로 구현하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct Rectangle {
width: i32,
height: i32
}
impl Rectangle {
fn compare(&self, x: &Rectangle) -> bool {
self.width >= x.width && self.height >= x.height
}
}

fn main() {
let rec1 = Rectangle {width: 40, height:30};
let rec2 = Rectangle {width: 50, height:30};
let rec3 = Rectangle {width: 20, height:30};

println!("rec1은 rec2를 가릴 수 있나요? {}", rec1.compare(&rec2));
println!("rec1는 rec3를 가릴 수 있나요? {}", rec1.compare(&rec3));
}

이제 코드의 관점으로 되돌아오자. 위 코드는 메서드로 compare()을 구현하였으며, 매개변수를 2개 사용하여 두 개의 사각형 정보를 입력하였다. 첫번째 매개변수의 경우 해당 메서드를 사용할 인스턴스의 값이기에 &self로 작성하였으며, 두번째의 경우는 어떤 데이터가 들어오는지 타입을 명확히 지정해주었다. 위 예시로 알 수 있듯이 매서드를 불러오는 인스턴스만 &self 그 뒤에 추가적으로 필요한 매개변수는 함수와 동일하게 작성하면 된다.

연관 함수

연관 함수란 해당 구조체 인스턴스를 매개변수로 사용하지는 않지만 프로그램 상 연관되어있는 함수들을 칭한다. 따라서 연관 함수는 메서드에 속하지 않는다. 다만 연관성을 나타내기 위해 impl 블록에 묶어서 정의할 수 있다. 연관 함수를 사용하는 방법은 impl이름::함수이름() 구조이며, 보통 해당 구조체 인스턴스를 리턴하는 생성자를 구현해야 할 때 자주 사용된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct Rectangle {
width: i32,
height: i32
}
impl Rectangle {
fn area(&self) -> i32 {
self.width * self.height
}
fn squre(x: i32) -> Rectangle {
Rectangle { width: x, height:x}
}
}

fn main() {
println!("변이 3인 정사각형의 면적: {}", Rectangle::squre(3).area());
}

위 코드는 squre()이라는 연관함수를 통해 Rectangle의 인스턴스를 생성하고 이에 대한 면적을 구하는 코드이다. 이와 같이 연관 함수는 메서드는 아니지만 impl 블록 내에서 정의되는 메서드와 연관이 있는 경우 메서드와 같이 블록 내부에 정의한다.

여러 개의 impl 블록

특정 구조체에 하나의 impl 블록만을 정의할 수 있는 것은 아니다. 상황에 따라서 블록을 구분하는게 코드가 더 명확할 때가 있기에 충분히 여러 개로 나누어 사용할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
struct Rectangle {
width: i32,
height: i32
}
impl Rectangle {
fn area(&self) -> i32 {
self.width * self.height
}
fn compare(&self, x: &Rectangle) -> bool {
self.width >= x.width && self.height >= x.height
}
}

impl Rectangle {
fn squre(x: i32) -> Rectangle {
Rectangle { width: x, height:x}
}
}

fn main() {
let sq3 = Rectangle::squre(3);
let sq5 = Rectangle::squre(5);
println!("변이 3인 정사각형의 면적: {}", sq3.area());
println!("sq3는 sq5보다 면적이 큰가요?: {}", sq3.compare(&sq5));
}

위 코드는 연관 함수와 메서드를 impl 블록으로 나누어 작성하였다. 물론 예시처럼 간단한 코드들은 하나의 impl 블록에 써도 상관없지만 가능하다는 것을 보기 위해 나눈 것일 뿐 어떤 규칙은 없다.