본문 바로가기

메서드 문법

impl 블록에서 self를 받는 메서드를 정의하고 추가 매개변수·연관 함수·여러 구현 블록으로 구조체 동작을 구성합니다.

메서드(method)는 함수와 유사합니다.

fn 키워드와 함수명으로 선언하고, 매개변수와 반환 값을 가지며, 다른 어딘가로부터 호출될 때 실행됩니다.

하지만 메서드는 함수와 달리 구조체 컨텍스트에 정의되고(열거형이나 트레이트 객체 안에 정의되기도 하며, 이는 각각 5장, 16장에서 알아보겠습니다), 첫 번째 매개변수가 항상 self라는 차이점이 있습니다.

self 매개변수는 메서드를 호출하고 있는 구조체 인스턴스를 나타냅니다.

함수 호출, 메서드 호출, 연관 함수의 차이를 한 흐름으로 정리해 보겠습니다. 먼저 앞 절의 Rectangle 정의와 area를 기준으로 읽습니다. main이 없는 impl 발췌는 기존 정의에 추가하되 같은 이름의 메서드를 중복해서 붙이지 않습니다. 자동 탐색의 자세한 설명은 모호한 호출을 만났을 때 되짚어 볼 규칙이며, 첫 실습에서는 self의 소유·대여와 호출 표기를 우선 확인하세요.


메서드 정의하기

메서드 수신자는 대여·수정·소비 계약을 정한다

본문의 별도 완전 예 square(3)→area 9에서 3×3 유지→enlarge(2)에서 6×6/36→into_dimensions 소비로 tuple 6,6의 동일 인스턴스 상태가 명시된다. 반환 튜플 사용 가능과 원 rect moved를 함께 보여 줄 가치가 높다.

Rust 메서드의 self, 공유 참조 self, 가변 참조 self가 각각 소유권 이동, 읽기, 변경 가능성과 호출 뒤 원본 사용 여부를 어떻게 정하는지 비교하는 표

RECEIVER CONTRACT · OWNERSHIP

수신자 형태가 읽기·변경·소유권 전달을 정한다

첫 매개변수의 형태는 단순한 표기 차이가 아닙니다. &self, &mut self, self가 메서드의 접근 권한과 호출 뒤 값의 상태를 정합니다.

square와 세 self 계약을 한 인스턴스의 상태 전환으로 연결한다7노드6간선. 새본문별도완전예 square3→rect3,3→공유area반환9원상태유지→가변enlarge2rect6,6넓이36→값selfinto_dimensions소비→반환tuple6,6. 소비노드의다른간선은원rect사용불가로향하며반환tuple사용가능과원본사용불가를동시에구분한다. 이Rectangle은Copy미구현이고치수곱은본문작은값범위다. mobile도같은전환·독립결과를보존하며tuple에서원본무효노드로시간간선을추가하지않는다.square(3)인스턴스 없는 호출새 Rectanglerect: 3 × 3area · 공유 대여넓이9 · rect는3 × 3enlarge(2)가변 대여: rect6 × 6넓이36into_dimensionsself 값으로 소비반환 튜플6 · 6 사용 가능원 rect 사용 불가Copy 아닌 값이 이동됨반환값원 바인딩읽기·변경은 소유권 유지소비 뒤 반환값과 원본을 구분
square와 세 self 계약을 한 인스턴스의 상태 전환으로 연결한다7노드6간선. 새본문별도완전예 square3→rect3,3→공유area반환9원상태유지→가변enlarge2rect6,6넓이36→값selfinto_dimensions소비→반환tuple6,6. 소비노드의다른간선은원rect사용불가로향하며반환tuple사용가능과원본사용불가를동시에구분한다. 이Rectangle은Copy미구현이고치수곱은본문작은값범위다. mobile도같은전환·독립결과를보존하며tuple에서원본무효노드로시간간선을추가하지않는다.square(3)인스턴스 없는 호출새 Rectanglerect: 3 × 3area · 공유 대여넓이9 · rect는3 × 3enlarge(2)가변 대여: rect6 × 6넓이36into_dimensionsself 값으로 소비반환 튜플6 · 6 사용 가능원 rect 사용 불가Copy 아닌 값이 이동됨반환값원 바인딩읽기·변경은 소유권 유지소비 뒤 반환값과 원본을 구분
Self 수신자 세 형태의 실제 계약
# 수신자와 허용되는 동작 호출자에게 남는 결과
1

공유 대여 · &self

self: &Self의 축약형입니다. 값을 읽되 공유 대여를 통해 독점 변경이나 필드 이동은 제한됩니다.

호출자가 계속 소유합니다. area의 u32 필드 읽기는 Copy라 값을 복사하지만, Copy가 아닌 필드는 공유 참조 밖으로 이동할 수 없습니다.
2

가변 대여 · &mut self

self: &mut Self의 축약형입니다. 읽기와 쓰기가 가능하지만 수신자를 독점적으로 대여합니다.

소유권은 호출자에게 남습니다. 다만 수신자를 가변으로 빌릴 수 있어야 하고, 호출과 충돌하는 다른 대여가 없어야 합니다.
3

값으로 받기 · self

self: Self의 축약형이며 일반적인 값 전달 규칙을 따릅니다. 변환 후 새 값을 반환하는 메서드에 자주 쓰입니다.

Copy가 아닌 이 절의 Rectangle은 이동되어 원래 바인딩을 다시 쓸 수 없습니다. 수신자 타입이 Copy라면 복사본이 전달되어 원본도 유효합니다.
&self · 공유 대여
읽기 중심 계약입니다. 호출자가 계속 소유하며, 이 예제의 u32 필드는 Copy라 값이 복사됩니다. Copy가 아닌 필드는 공유 참조 밖으로 이동할 수 없습니다.
&mut self · 가변 대여
읽고 쓸 수 있지만 독점 대여입니다. 수신자를 가변으로 빌릴 수 있고 충돌하는 대여가 없어야 합니다.
self · 값으로 받기
Copy가 아닌 값은 이동합니다. Copy 타입이라면 복사본이 전달되어 원본도 계속 쓸 수 있습니다.
추가 매개변수는 별도 계약
can_hold의 other: &Rectangle처럼 self 뒤의 매개변수는 일반 함수 매개변수와 같습니다.
점 호출은 수신자만 메서드 시그니처에 맞춰 자동 보정합니다. rect1.can_hold(&rect2)의 추가 인수 &rect2는 호출자가 명시합니다.

이 예의 조건

  • 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
  • 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
관계도의 보충 설명
원 도표의 제목·노드·관계·조건·주석
범위원문
관계·조건 설명7노드와 6간선이다. 새 본문의 별도 완전한 예인 square 3→rect 3,3→공유 area 반환 9와 원래 상태 유지→가변 enlarge 2로 rect 6,6과 넓이 36→값 self를 받는 into_dimensions 소비→반환 tuple 6,6의 흐름이다. 소비 노드의 다른 간선은 원래 rect 사용 불가로 향하며 반환 tuple 사용 가능과 원본 사용 불가를 동시에 구분한다. 이 Rectangle은 Copy 미구현이고 치수의 곱은 본문의 작은 값 범위다. 세로 도표도 같은 전환과 독립 결과를 보존하며 tuple에서 원본 무효 노드로 시간 간선을 추가하지 않는다.
노드·연결선 라벨넓이 9 · rect는 3 × 3
노드·연결선 라벨가변 대여: rect 6 × 6
노드·연결선 라벨넓이 36
예의 값과 변화
관찰·상황값·보충 정보변화·이유
생성·읽기

square(3)으로 만든 rect를 공유 메서드 area로 읽는다.

  • rect: 3×3
  • area: 9
  • 공유 메서드는 원 치수를 바꾸지 않는다.
가변 갱신

enlarge(2)가 같은 rect의 치수를 늘린다.

  • rect: 6×6
  • area: 36
  • 가변 대여는 소유권을 유지하면서 저장 상태를 바꾼다.
값 소비

into_dimensions가 self를 소비해 튜플을 준다.

  • 반환: (6,6)
  • rect: moved·사용 불가
  • Copy 미구현 Rectangle의 값 수신자는 원 바인딩을 소비한다.

메서드 수신자는 대여·수정·소비 계약을 정한다

본문의 별도 완전 예 square(3)→area 9에서 3×3 유지→enlarge(2)에서 6×6/36→into_dimensions 소비로 tuple 6,6의 동일 인스턴스 상태가 명시된다. 반환 튜플 사용 가능과 원 rect moved를 함께 보여 줄 가치가 높다.

Rust 메서드의 self, 공유 참조 self, 가변 참조 self가 각각 소유권 이동, 읽기, 변경 가능성과 호출 뒤 원본 사용 여부를 어떻게 정하는지 비교하는 표

RECEIVER CONTRACT · OWNERSHIP

수신자 형태가 읽기·변경·소유권 전달을 정한다

첫 매개변수의 형태는 단순한 표기 차이가 아닙니다. &self, &mut self, self가 메서드의 접근 권한과 호출 뒤 값의 상태를 정합니다.

square와 세 self 계약을 한 인스턴스의 상태 전환으로 연결한다7노드6간선. 새본문별도완전예 square3→rect3,3→공유area반환9원상태유지→가변enlarge2rect6,6넓이36→값selfinto_dimensions소비→반환tuple6,6. 소비노드의다른간선은원rect사용불가로향하며반환tuple사용가능과원본사용불가를동시에구분한다. 이Rectangle은Copy미구현이고치수곱은본문작은값범위다. mobile도같은전환·독립결과를보존하며tuple에서원본무효노드로시간간선을추가하지않는다.square(3)인스턴스 없는 호출새 Rectanglerect: 3 × 3area · 공유 대여넓이9 · rect는3 × 3enlarge(2)가변 대여: rect6 × 6넓이36into_dimensionsself 값으로 소비반환 튜플6 · 6 사용 가능원 rect 사용 불가Copy 아닌 값이 이동됨반환값원 바인딩읽기·변경은 소유권 유지소비 뒤 반환값과 원본을 구분
square와 세 self 계약을 한 인스턴스의 상태 전환으로 연결한다7노드6간선. 새본문별도완전예 square3→rect3,3→공유area반환9원상태유지→가변enlarge2rect6,6넓이36→값selfinto_dimensions소비→반환tuple6,6. 소비노드의다른간선은원rect사용불가로향하며반환tuple사용가능과원본사용불가를동시에구분한다. 이Rectangle은Copy미구현이고치수곱은본문작은값범위다. mobile도같은전환·독립결과를보존하며tuple에서원본무효노드로시간간선을추가하지않는다.square(3)인스턴스 없는 호출새 Rectanglerect: 3 × 3area · 공유 대여넓이9 · rect는3 × 3enlarge(2)가변 대여: rect6 × 6넓이36into_dimensionsself 값으로 소비반환 튜플6 · 6 사용 가능원 rect 사용 불가Copy 아닌 값이 이동됨반환값원 바인딩읽기·변경은 소유권 유지소비 뒤 반환값과 원본을 구분
Self 수신자 세 형태의 실제 계약
# 수신자와 허용되는 동작 호출자에게 남는 결과
1

공유 대여 · &self

self: &Self의 축약형입니다. 값을 읽되 공유 대여를 통해 독점 변경이나 필드 이동은 제한됩니다.

호출자가 계속 소유합니다. area의 u32 필드 읽기는 Copy라 값을 복사하지만, Copy가 아닌 필드는 공유 참조 밖으로 이동할 수 없습니다.
2

가변 대여 · &mut self

self: &mut Self의 축약형입니다. 읽기와 쓰기가 가능하지만 수신자를 독점적으로 대여합니다.

소유권은 호출자에게 남습니다. 다만 수신자를 가변으로 빌릴 수 있어야 하고, 호출과 충돌하는 다른 대여가 없어야 합니다.
3

값으로 받기 · self

self: Self의 축약형이며 일반적인 값 전달 규칙을 따릅니다. 변환 후 새 값을 반환하는 메서드에 자주 쓰입니다.

Copy가 아닌 이 절의 Rectangle은 이동되어 원래 바인딩을 다시 쓸 수 없습니다. 수신자 타입이 Copy라면 복사본이 전달되어 원본도 유효합니다.
&self · 공유 대여
읽기 중심 계약입니다. 호출자가 계속 소유하며, 이 예제의 u32 필드는 Copy라 값이 복사됩니다. Copy가 아닌 필드는 공유 참조 밖으로 이동할 수 없습니다.
&mut self · 가변 대여
읽고 쓸 수 있지만 독점 대여입니다. 수신자를 가변으로 빌릴 수 있고 충돌하는 대여가 없어야 합니다.
self · 값으로 받기
Copy가 아닌 값은 이동합니다. Copy 타입이라면 복사본이 전달되어 원본도 계속 쓸 수 있습니다.
추가 매개변수는 별도 계약
can_hold의 other: &Rectangle처럼 self 뒤의 매개변수는 일반 함수 매개변수와 같습니다.
점 호출은 수신자만 메서드 시그니처에 맞춰 자동 보정합니다. rect1.can_hold(&rect2)의 추가 인수 &rect2는 호출자가 명시합니다.

이 예의 조건

  • 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
  • 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.

관계도의 보충 설명

원 도표의 제목·노드·관계·조건·주석
범위원문
관계·조건 설명7노드와 6간선이다. 새 본문의 별도 완전한 예인 square 3→rect 3,3→공유 area 반환 9와 원래 상태 유지→가변 enlarge 2로 rect 6,6과 넓이 36→값 self를 받는 into_dimensions 소비→반환 tuple 6,6의 흐름이다. 소비 노드의 다른 간선은 원래 rect 사용 불가로 향하며 반환 tuple 사용 가능과 원본 사용 불가를 동시에 구분한다. 이 Rectangle은 Copy 미구현이고 치수의 곱은 본문의 작은 값 범위다. 세로 도표도 같은 전환과 독립 결과를 보존하며 tuple에서 원본 무효 노드로 시간 간선을 추가하지 않는다.
노드·연결선 라벨넓이 9 · rect는 3 × 3
노드·연결선 라벨가변 대여: rect 6 × 6
노드·연결선 라벨넓이 36
예의 값과 변화
관찰·상황값·보충 정보변화·이유
생성·읽기

square(3)으로 만든 rect를 공유 메서드 area로 읽는다.

  • rect: 3×3
  • area: 9
  • 공유 메서드는 원 치수를 바꾸지 않는다.
가변 갱신

enlarge(2)가 같은 rect의 치수를 늘린다.

  • rect: 6×6
  • area: 36
  • 가변 대여는 소유권을 유지하면서 저장 상태를 바꾼다.
값 소비

into_dimensions가 self를 소비해 튜플을 준다.

  • 반환: (6,6)
  • rect: moved·사용 불가
  • Copy 미구현 Rectangle의 값 수신자는 원 바인딩을 소비한다.

기존의 Rectangle 매개변수를 갖던 area 함수를 수정하여 예제 4-13처럼 Rectangle 구조체에 정의된 area 메서드로 바꿔봅시다.

예제 4-13: Rectangle 구조체에 area 메서드 정의하기
src/main.rs
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

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

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    println!(
        "The area of the rectangle is {} square pixels.",
        rect1.area()
    );
}

Rectangle의 컨텍스트에 함수를 정의하기 위해서, Rectangle에 대한 impl(implementation, 구현) 블록을 만드는 것으로 시작합니다.

이 impl 블록 내의 모든 것은 Rectangle 타입과 연관됩니다.

그런 다음 area 함수를 impl의 중괄호 안으로 옮기고 함수 시그니처의 첫 번째 매개변수를 (이번 경우는 유일한 매개변수군요) self로 변경하고 본문 내의 나머지 모든 부분도 변경합니다.

그리고 main 함수 내에서는 rect1을 인수로 전달하여 area 함수를 호출했었는데, 그 대신 메서드 문법(method syntax)을 사용해 Rectangle 인스턴스의 area 메서드를 호출할 수 있습니다.

메서드 문법은 차례대로 인스턴스, 점, 메서드명, 괄호 및 인수로 구성됩니다.

area 시그니처에서는 rectangle: &Rectangle 대신 &self를 사용했습니다.

&self는 실제로는 self: &Self를 줄인 것입니다.

impl 블록 내에서 Self는 impl 블록의 대상이 되는 타입의 별칭입니다.

메서드는 Self 타입의 self라는 이름의 매개변수를 첫 번째 매개변수로 가져야 하는데, 그렇게 해야 첫 번째 매개변수 자리에 적어 넣은 self 형태의 축약형을 사용할 수 있습니다.

rectangle: &Rectangle에서 그랬던 것처럼 이 메서드가 Self의 인스턴스를 빌려온다는 것을 나타내기 위해서는 self 축약형 앞에 &를 계속 붙여둘 필요가 있음을 주목하세요.

메서드는 다른 매개변수가 그런 것처럼 self의 소유권을 가져올 수도, 지금처럼 self를 불변으로 빌려올 수도, 가변으로 빌려올 수도 있습니다.

여기서 &self를 선택한 이유는 기존의 함수 버전에서 &Rectangle을 사용했던 이유와 같습니다.

지금 원하는 것이 소유권을 가져오는 것도, 데이터를 쓰는 것도 아닌, 데이터를 읽는 것뿐이니까요.

만약 메서드에서 작업 중 호출한 인스턴스를 변경하고 싶다면, 첫 번째 매개변수로 &mut self를 사용하면 됩니다.

self라고만 작성하여 인스턴스의 소유권을 가져오도록 만드는 일은 거의 없습니다; 이러한 기법은 보통 해당 메서드가 self를 다른 무언가로 변환하고 그 이후에는 원본 인스턴스의 사용을 막고자 할 때 사용됩니다.

값으로 받는 self에도 일반적인 이동 규칙이 적용됩니다. 이 절의 Rectangle은 Copy를 구현하지 않았으므로 이런 메서드를 호출하면 값이 이동하여 원래 바인딩을 다시 사용할 수 없습니다. 반대로 수신자 타입이 Copy라면 메서드는 복사본을 받고 원래 값은 계속 사용할 수 있습니다.

&self인 area에서 self.width와 self.height를 값으로 읽을 수 있는 것은 두 필드가 Copy인 u32이기 때문입니다. 공유 대여한 값의 Copy가 아닌 필드는 밖으로 이동시킬 수 없으며 참조로 사용해야 합니다.

함수 대신 메서드를 사용하는 주된 이유는 메서드 구문을 제공하고 모든 메서드 시그니처 내에서 self 타입을 반복할 필요가 없다는 것 외에도 코드를 더 조직적으로 만들기 위해서입니다.

향후 우리가 제공한 라이브러리를 사용할 사람들이 Rectangle의 기능과 관련된 코드를 라이브러리 곳곳에서 찾아내야 하는 것보다는, 하나의 impl 블록 내에 이 타입의 인스턴스로 할 수 있는 모든 것들을 모아두는 것이죠.

구조체의 필드 이름과 동일한 이름의 메서드를 만들 수도 있습니다.

예를 들면, width라는 중복된 이름의 메서드를 Rectangle 상에 정의할 수 있지요.

src/main.rs
impl Rectangle {
    fn width(&self) -> bool {
        self.width > 0
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    if rect1.width() {
        println!("The rectangle has a nonzero width; it is {}", rect1.width);
    }
}

여기서는 인스턴스의 width 필드가 0보다 크면 true를 반환하고 0이면 false를 반환하는 메서드의 이름으로 width를 선택했습니다.

같은 이름의 메서드 내에서 필드를 어떤 목적으로든 사용할 수 있습니다.

main에서 rect1.width 뒤에 괄호를 붙이면 러스트는 width 메서드를 의도한다는 것을 인지합니다.

괄호를 사용하지 않으면 러스트는 width 필드를 의미한다는 것으로 봅니다.

필드와 동일한 이름의 메서드를 만드는 경우는 해당 필드의 값을 얻어오는 것 말고는 안 하는 경우가 대부분이긴 합니다.

이러한 메서드를 게터(getter)라고 부르는데, 러스트는 다른 언어들처럼 구조체 필드에 대한 게터를 자동으로 만들지 않습니다.

필드를 비공개(private)로 하고 메서드는 공개(public)로 만들 수 있기 때문에 게터는 어떤 타입의 공개 API로서 어떤 필드에 대해 읽기 전용 접근만 허용하고자 하는 경우 유용합니다.

공개와 비공개가 무엇이고, 필드 혹은 메서드를 공개 혹은 비공개로 만드는 방법에 대해서는 6장에서 다루겠습니다.


-> 연산자는 없나요?

C나 C++ 언어에서는 메서드 호출에 두 종류의 연산자가 쓰입니다. 어떤 객체의 메서드를 직접 호출할 땐 .를 사용하고, 어떤 객체의 포인터를 이용해 메서드를 호출하는 중이라서 역참조가 필요할 땐 ->를 사용하죠. 예를 들어서 object라는 포인터가 있다면, object->something()는 (*object).something()로 나타낼 수 있습니다.

이 -> 연산자와 동일한 기능을 하는 연산자는 러스트에 없습니다. 러스트에는 자동 참조 및 역참조(automatic referencing and dereferencing)라는 기능이 있고, 메서드 호출에 이 기능이 포함되어 있기 때문입니다.

여러분이 object.something() 코드로 메서드를 호출하면, 러스트에서 자동으로 해당 메서드의 시그니처에 맞도록 &, &mut, *를 추가합니다. 즉, 다음 두 표현은 서로 같은 표현입니다.

이 발췌에서 각 줄 맨 앞의 # 는 Rust Book의 숨김 준비 코드 표식입니다. 직접 시험할 때는 이 표식만 제거하고 #[derive(...)]는 남깁니다. Point와 impl 정의를 함께 두고, p1·p2 생성과 두 distance 호출은 main 안에 넣습니다.

# #[derive(Debug,Copy,Clone)]
# struct Point {
#     x: f64,
#     y: f64,
# }
#
# impl Point {
#    fn distance(&self, other: &Point) -> f64 {
#        let x_squared = f64::powi(other.x - self.x, 2);
#        let y_squared = f64::powi(other.y - self.y, 2);
#
#        f64::sqrt(x_squared + y_squared)
#    }
# }
# let p1 = Point { x: 0.0, y: 0.0 };
# let p2 = Point { x: 5.0, y: 6.5 };
p1.distance(&p2);
(&p1).distance(&p2);

첫 번째 표현이 더 깔끔하죠? 이런 자동 참조 동작은 메서드의 수신자(self의 타입을 말합니다)가 명확하기 때문에 가능합니다. 수신자와 메서드명을 알면 해당 메서드가 인스턴스를 읽기만 하는지(&self), 변경하는지(&mut self), 소비하는지(self) 러스트가 알아낼 수 있거든요. 또한 메서드의 수신자를 러스트에서 암묵적으로 빌린다는 점은 실제로 소유권을 다루기 쉽게 만드는 중요한 부분입니다.

이 자동 보정은 점 문법의 수신자를 찾는 과정에 적용됩니다. 러스트는 수신자 타입을 반복해서 역참조하며 후보를 만들고, 마지막에는 배열을 슬라이스로 변환하는 후보도 시도합니다. 각 후보 T 바로 뒤에 &T와 &mut T를 추가하고, 후보 순서대로 inherent 메서드와 보이는 트레이트 메서드를 탐색합니다. 타입 매개변수에서는 트레이트 경계에 명시된 메서드를 우선합니다. 후보가 없거나 해당 탐색 단계에서 선택이 모호하면 컴파일 오류가 납니다. 메서드를 선택한 뒤에는 가변성, 수명, 대여 규칙과 unsafe 호출 조건을 확인합니다. 이 조건을 만족하지 못해도 다음 후보를 다시 탐색하지 않습니다.

다른 매개변수까지 자동으로 대여해 주는 것은 아닙니다. 예를 들어 rect1.can_hold(&rect2)에서 수신자 rect1은 &self에 맞춰 자동으로 빌리지만, 추가 매개변수인 rect2의 &는 호출자가 직접 작성합니다.

아래 다이어그램은 메서드 호출 시 러스트가 수신자 타입에 맞춰 자동으로 참조와 역참조를 보정하는 과정을 정리한 것입니다.

점 호출은 수신자 후보를 만든 뒤 메서드를 결정한다

점 메서드의 후보 생성·T/&T/&mut T 후보 순서 탐색·선택 뒤 가변성 확인은 명시된 탐색 순서다. 한 receiver의 후보 장부로 선택을 추적하되 컴파일러 구현 pass나 런타임 탐색으로 만들지 않고 선택 뒤 조건 실패의 재탐색 금지를 보존한다.

Rust 점 메서드 호출이 수신자 타입의 반복 역참조 후보와 각 후보의 공유·가변 참조 형태를 만든 뒤 보이는 메서드를 찾고, 유일하게 호출 가능하면 실행하며 그렇지 않으면 컴파일 오류가 되는 흐름도

METHOD LOOKUP · AUTOREF / AUTODEREF

메서드는 후보 순서대로 찾고 선택 뒤 호출 조건을 검사한다

점 문법은 수신자에 무조건 & 하나를 붙이는 단축 규칙이 아닙니다. 수신자 타입의 역참조 사슬과 대여 후보를 순서대로 만들고 보이는 메서드를 탐색합니다.

원 메서드 탐색의 후보 순서와 호출 성공·오류 분기를 모바일에 보존한다원desktop7노드6간선. 점호출→반복역참조/최종배열슬라이스후보→각T뒤&T/&mutT추가→후보순서별inherent/보이는trait/타입매개변수bound우선탐색→선택과호출조건→성공호출. 조건아니요는성공노드밖우회선으로컴파일오류에향한다. 후보없음/모호함/선택뒤가변성수명unsafe조건위반을원본문과동일하게구분하고선택후실패시재탐색하지않는다. 추가인수대여는수신자자동보정과별개다.점 메서드 호출receiver.method(args)수신자 타입 후보반복 역참조 사슬끝에 배열→슬라이스참조 후보 추가T · &T · &mut T후보 순서 탐색각 후보: inherent보이는 trait 메서드타입 매개변수는bound 메서드 우선선택·호출 조건가변성·수명·unsafe충족하는가?메서드 호출수신자 형태 보정추가 인수는 직접 작성컴파일 오류없음·모호함·조건 위반다음 후보 재탐색 안 함예아니요선택 뒤 조건 실패하면다음 후보를 다시 찾지 않음
Rust 메서드 호출의 자동 참조와 역참조 탐색 수신자를 반복 역참조하고 마지막 배열 슬라이스 변환을 시도한 뒤 각 후보 바로 뒤에 참조 후보를 추가합니다. 후보 순서대로 메서드를 찾아 탐색 단계에서 선택하고, 선택 뒤 가변성·라이프타임·unsafe 조건을 검사합니다. 탐색이나 호출 조건이 실패하면 컴파일 오류이며 호출 조건 실패 뒤 다음 후보를 재탐색하지 않습니다. 점 메서드 호출receiver.method(args)1 · 타입 후보를 만든다T → Deref::Target → …마지막에 배열 → 슬라이스도 시도2 · 각 후보 뒤에 참조 후보T · &T · &mut T3 · 후보 순서대로 메서드 탐색각 후보: inherent → 보이는 trait타입 매개변수의 bound를 우선 탐색탐색 단계에서 선택·모호함을 판정선택이 명확하고호출 조건도 만족?YESNO메서드 호출receiver를 맞는 형태로 보정추가 인수는 호출자가 명시컴파일 오류후보 없음 · 모호함 · 호출 조건 위반선택 후 실패해도 다음 후보로 안 감
  1. receiver.method(args)에서 먼저 수신자 표현식의 타입을 얻습니다.

  2. 수신자 타입을 반복해서 역참조하며 후보 타입을 만듭니다. 마지막에 배열을 슬라이스로 변환하는 후보도 시도합니다.

  3. 각 후보 T 바로 뒤에 &T와 &mut T를 추가합니다.

  4. 후보 순서대로 inherent 메서드와 보이는 trait 메서드를 탐색합니다. 타입 매개변수에서는 trait bound의 메서드를 우선합니다. 모호함은 해당 탐색 단계에서 판단합니다.

  5. 선택한 메서드가 가변성·수명·대여·unsafe 조건상 실제로 호출 가능한지 검사합니다. 실패해도 다음 후보를 다시 찾지 않습니다.

선택이 명확하고 호출 가능

수신자를 맞는 형태로 자동 보정해 메서드를 호출합니다.

오류

후보가 없거나 모호하거나 선택한 메서드의 호출 조건을 만족하지 않으면 컴파일 오류입니다.

자동 참조와 역참조는 점 호출의 수신자에 대한 탐색입니다. 추가 인수는 호출자가 명시하며, 이름이 모호한 경우에는 완전 한정 함수 호출 문법으로 구분해야 할 수 있습니다.

이 예의 조건

  • 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
  • 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
관계도의 보충 설명
원 도표의 제목·노드·관계·조건·주석
범위원문
관계·조건 설명원래 가로 도표의 7노드와 6간선이다. 점 호출→반복 역참조/최종 배열 슬라이스 후보→각 T 뒤에 &T/&mut T 추가→후보 순서별 inherent/보이는 trait/타입 매개변수 bound 우선 탐색→선택과 호출 조건→성공 호출의 흐름이다. 조건이 아니요인 경우 성공 노드 밖의 우회선으로 컴파일 오류를 향한다. 후보 없음/모호함/선택 뒤 가변성·수명·unsafe 조건 위반을 원래 본문과 동일하게 구분하고 선택 후 실패 시 재탐색하지 않는다. 추가 인수의 대여는 수신자 자동 보정과 별개다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
수신자 후보

rect.area 호출의 Rectangle 후보를 만든다.

  • 후보 순서: Rectangle, &Rectangle, &mut Rectangle
  • 근거: 역참조·참조 후보 규칙
  • 이것은 호출 해석 장부이며 런타임 탐색이나 내부 pass 시간표가 아니다.
메서드 선택

&self area에 맞는 후보를 찾는다.

  • 수신자: &Rectangle
  • 메서드: area(&self)
  • 후보 순서와 보이는 inherent/trait 메서드에 따라 선택한다.
조건 검사·호출

대여 조건이 맞으면 수신자를 보정해 호출한다.

  • 조건: 가변성·수명·unsafe 적합
  • 추가 인수: 호출자 직접 작성
  • 선택 뒤 조건 실패를 다음 후보 재탐색으로 해결하지 않는다.

점 호출은 수신자 후보를 만든 뒤 메서드를 결정한다

점 메서드의 후보 생성·T/&T/&mut T 후보 순서 탐색·선택 뒤 가변성 확인은 명시된 탐색 순서다. 한 receiver의 후보 장부로 선택을 추적하되 컴파일러 구현 pass나 런타임 탐색으로 만들지 않고 선택 뒤 조건 실패의 재탐색 금지를 보존한다.

Rust 점 메서드 호출이 수신자 타입의 반복 역참조 후보와 각 후보의 공유·가변 참조 형태를 만든 뒤 보이는 메서드를 찾고, 유일하게 호출 가능하면 실행하며 그렇지 않으면 컴파일 오류가 되는 흐름도

METHOD LOOKUP · AUTOREF / AUTODEREF

메서드는 후보 순서대로 찾고 선택 뒤 호출 조건을 검사한다

점 문법은 수신자에 무조건 & 하나를 붙이는 단축 규칙이 아닙니다. 수신자 타입의 역참조 사슬과 대여 후보를 순서대로 만들고 보이는 메서드를 탐색합니다.

원 메서드 탐색의 후보 순서와 호출 성공·오류 분기를 모바일에 보존한다원desktop7노드6간선. 점호출→반복역참조/최종배열슬라이스후보→각T뒤&T/&mutT추가→후보순서별inherent/보이는trait/타입매개변수bound우선탐색→선택과호출조건→성공호출. 조건아니요는성공노드밖우회선으로컴파일오류에향한다. 후보없음/모호함/선택뒤가변성수명unsafe조건위반을원본문과동일하게구분하고선택후실패시재탐색하지않는다. 추가인수대여는수신자자동보정과별개다.점 메서드 호출receiver.method(args)수신자 타입 후보반복 역참조 사슬끝에 배열→슬라이스참조 후보 추가T · &T · &mut T후보 순서 탐색각 후보: inherent보이는 trait 메서드타입 매개변수는bound 메서드 우선선택·호출 조건가변성·수명·unsafe충족하는가?메서드 호출수신자 형태 보정추가 인수는 직접 작성컴파일 오류없음·모호함·조건 위반다음 후보 재탐색 안 함예아니요선택 뒤 조건 실패하면다음 후보를 다시 찾지 않음
Rust 메서드 호출의 자동 참조와 역참조 탐색 수신자를 반복 역참조하고 마지막 배열 슬라이스 변환을 시도한 뒤 각 후보 바로 뒤에 참조 후보를 추가합니다. 후보 순서대로 메서드를 찾아 탐색 단계에서 선택하고, 선택 뒤 가변성·라이프타임·unsafe 조건을 검사합니다. 탐색이나 호출 조건이 실패하면 컴파일 오류이며 호출 조건 실패 뒤 다음 후보를 재탐색하지 않습니다. 점 메서드 호출receiver.method(args)1 · 타입 후보를 만든다T → Deref::Target → …마지막에 배열 → 슬라이스도 시도2 · 각 후보 뒤에 참조 후보T · &T · &mut T3 · 후보 순서대로 메서드 탐색각 후보: inherent → 보이는 trait타입 매개변수의 bound를 우선 탐색탐색 단계에서 선택·모호함을 판정선택이 명확하고호출 조건도 만족?YESNO메서드 호출receiver를 맞는 형태로 보정추가 인수는 호출자가 명시컴파일 오류후보 없음 · 모호함 · 호출 조건 위반선택 후 실패해도 다음 후보로 안 감
  1. receiver.method(args)에서 먼저 수신자 표현식의 타입을 얻습니다.

  2. 수신자 타입을 반복해서 역참조하며 후보 타입을 만듭니다. 마지막에 배열을 슬라이스로 변환하는 후보도 시도합니다.

  3. 각 후보 T 바로 뒤에 &T와 &mut T를 추가합니다.

  4. 후보 순서대로 inherent 메서드와 보이는 trait 메서드를 탐색합니다. 타입 매개변수에서는 trait bound의 메서드를 우선합니다. 모호함은 해당 탐색 단계에서 판단합니다.

  5. 선택한 메서드가 가변성·수명·대여·unsafe 조건상 실제로 호출 가능한지 검사합니다. 실패해도 다음 후보를 다시 찾지 않습니다.

선택이 명확하고 호출 가능

수신자를 맞는 형태로 자동 보정해 메서드를 호출합니다.

오류

후보가 없거나 모호하거나 선택한 메서드의 호출 조건을 만족하지 않으면 컴파일 오류입니다.

자동 참조와 역참조는 점 호출의 수신자에 대한 탐색입니다. 추가 인수는 호출자가 명시하며, 이름이 모호한 경우에는 완전 한정 함수 호출 문법으로 구분해야 할 수 있습니다.

이 예의 조건

  • 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
  • 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.

관계도의 보충 설명

원 도표의 제목·노드·관계·조건·주석
범위원문
관계·조건 설명원래 가로 도표의 7노드와 6간선이다. 점 호출→반복 역참조/최종 배열 슬라이스 후보→각 T 뒤에 &T/&mut T 추가→후보 순서별 inherent/보이는 trait/타입 매개변수 bound 우선 탐색→선택과 호출 조건→성공 호출의 흐름이다. 조건이 아니요인 경우 성공 노드 밖의 우회선으로 컴파일 오류를 향한다. 후보 없음/모호함/선택 뒤 가변성·수명·unsafe 조건 위반을 원래 본문과 동일하게 구분하고 선택 후 실패 시 재탐색하지 않는다. 추가 인수의 대여는 수신자 자동 보정과 별개다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
수신자 후보

rect.area 호출의 Rectangle 후보를 만든다.

  • 후보 순서: Rectangle, &Rectangle, &mut Rectangle
  • 근거: 역참조·참조 후보 규칙
  • 이것은 호출 해석 장부이며 런타임 탐색이나 내부 pass 시간표가 아니다.
메서드 선택

&self area에 맞는 후보를 찾는다.

  • 수신자: &Rectangle
  • 메서드: area(&self)
  • 후보 순서와 보이는 inherent/trait 메서드에 따라 선택한다.
조건 검사·호출

대여 조건이 맞으면 수신자를 보정해 호출한다.

  • 조건: 가변성·수명·unsafe 적합
  • 추가 인수: 호출자 직접 작성
  • 선택 뒤 조건 실패를 다음 후보 재탐색으로 해결하지 않는다.

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

Rectangle 구조체의 두 번째 메서드를 구현하여 메서드 사용법을 연습해봅시다.

이번에 만들 새로운 메서드는 다른 Rectangle 인스턴스를 받아서, self 사각형(첫 번째 Rectangle) 면적 내에 두 번째 사각형 Rectangle 인스턴스가 완전히 들어갈 수 있다면 true를 반환하고, 못 들어가면 false를 반환할 겁니다.

즉, can_hold 메서드를 정의하여 다음 예제 4-14에 나오는 프로그램이 작동하도록 만들겠습니다.

예제 4-14: can_hold 메서드를 작성하고 나면 작동할 코드
src/main.rs
fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };
    let rect2 = Rectangle {
        width: 10,
        height: 40,
    };
    let rect3 = Rectangle {
        width: 60,
        height: 45,
    };

    println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
    println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}

rect2는 너비와 높이 둘 다 rect1보다 작지만, rect3는 rect1보다 너비가 넓으므로 출력은 다음과 같을 겁니다.

Can rect1 hold rect2? true
Can rect1 hold rect3? false

메서드의 정의는 impl Rectangle 블록 내에 위치할 것이고, 메서드명은 can_hold, 매개변수는 Rectangle을 불변 참조자로 받겠죠.

이때 매개변수 타입은 메서드를 호출하는 코드를 보면 알아낼 수 있습니다.

rect1.can_hold(&rect2)에서 Rectangle 인스턴스 rect2의 불변 참조자인 &rect2를 전달했으니까요.

rect2를 읽을 수만 있으면 되기 때문에 가변으로 빌려올 필요도 없으며, rect2의 소유권을 main에 남겨두지 않을 이유도 없으니, 논리적으로도 불변 참조자가 가장 적합합니다.

반환 값은 부울린 타입이 될 것이고, self의 너비, 높이가 다른 Rectangle의 너비, 높이보다 큰지 검사하는 형태로 구현될 겁니다.

그럼 이제 예제 4-13의 impl 블록에 can_hold 메서드를 새로 추가해 보죠!

추가하고 난 모습은 다음 예제 4-15와 같습니다.

예제 4-15: 다른 Rectangle 인스턴스를 매개변수로 갖는 can_hold 메서드를 Rectangle에 구현
src/main.rs
impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }

    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

이제 예제 4-14에서 본 main 함수를 실행하면 원하던 결과가 나올 겁니다. 이 can_hold는 두 치수가 각각 엄격히 더 작아야 한다는 계약입니다. 같아도 들어가는지, 회전을 허용하는지는 이 구현에서 자동으로 처리하지 않습니다. &&는 너비 조건이 false면 높이 비교를 실행하지 않고, 넓이를 비교하는 함수도 아닙니다.

스스로 확인하기 30×50인 rect1에 대해 30×20, 40×20, 20×40인 세 사각형을 각각 검사하면 어떤 결과인가요? 면적이 작은 40×20도 왜 결과가 다를 수 있을까요? 회전을 새로 허용하는 요구가 생긴다면 어느 비교를 추가로 설계해야 할까요?

각 치수 조건의 풀이

순서대로 false, false, true입니다. 첫 번째는 너비 30이 같아 30 > 30에서 실패하고, 두 번째는 40이 더 넓어 실패합니다. 세 번째는 두 치수가 각각 작습니다. 원 코드는 회전하지 않으며 치수 20×40으로 바꾼 별도 사각형이 들어가는 것과 자동 회전 지원을 구분해야 합니다. 회전을 허용할 때는 width와 height를 맞바꾼 두 번째 방향도 비교하여 어느 한 방향이 계약을 만족하는지 설계할 수 있습니다. 0 같은 치수가 도메인에서 유효한지도 별도로 정하며 현재 메서드는 그런 입력 검증을 구현하지 않았습니다.

이처럼 메서드는 self 매개변수 뒤에 여러 매개변수를 가질 수 있으며, 이 매개변수는 함수에서의 매개변수와 동일하게 기능합니다.


연관 함수

impl 블록 내에 구현된 모든 함수를 연관 함수(associated function)라고 부르는데, 이는 impl 뒤에 나오는 타입과 모두 연관된 함수이기 때문입니다.

동작하는 데 해당 타입의 인스턴스가 필요하지 않다면 self를 첫 매개변수로 갖지 않는 (따라서 메서드가 아닌) 연관 함수를 정의할 수도 있습니다.

우리는 이미 String 타입에 정의되어 있는 String::from 함수처럼 이런 종류의 함수를 사용해 봤습니다.

메서드가 아닌 연관 함수는 구조체의 새 인스턴스를 반환하는 생성자로 자주 활용됩니다.

이 함수들은 보통 new라고 명명되는데, new는 이 언어에서 특별한 이름 혹은 키워드가 아닙니다.

생성자의 예시로, Rectangle로 정사각형을 만들 때 너비, 높이에 같은 값을 두 번 지정하지 않고 치수 하나를 매개변수로 받아서 해당 치수로 너비와 높이를 설정하는 연관 함수 square를 만들어서, 더 간단하게 정사각형을 만드는 기능을 제공해보겠습니다.

src/main.rs
impl Rectangle {
    fn square(size: u32) -> Self {
        Self {
            width: size,
            height: size,
        }
    }
}

반환 타입 및 함수 본문의 Self 키워드는 impl 키워드 뒤에 적혀있는 타입의 별칭으로서, 여기서는 Rectangle이 되겠습니다.

연관 함수를 호출할 땐 let sq = Rectangle::square(3);처럼 구조체 명에 :: 구문을 붙여서 호출합니다.

연관 함수는 구조체의 네임스페이스 안에 있기 때문이죠.

:: 구문은 6장에서 알아볼 모듈에 의해 생성되는 네임스페이스에도 사용됩니다.

메서드와 연관 함수는 self의 유무, 호출 위치, 소유권 처리 방식으로 함께 비교해 두면 더 쉽게 구분할 수 있습니다.

Rust impl 블록 안의 메서드와 self 없는 연관 함수, Self 별칭, 여러 impl 블록, 필드와 같은 이름인 메서드의 호출 방식과 역할을 비교하는 표

ASSOCIATED API · IMPL LAYOUT

메서드와 연관 함수는 호출할 때 수신자를 쓰는지가 다르다

impl Rectangle 안의 함수는 모두 타입에 연관됩니다. 그중 첫 매개변수가 self인 함수가 메서드이고, self가 없는 함수도 연관 함수로 같은 API에 속합니다.

Rectangle에 동작을 연결하는 네 가지 관점
# 정의와 호출 읽는 기준
1

메서드

fn area · &self

rect.area()

기존 인스턴스를 수신자로 사용합니다. self 형태가 읽기, 변경, 소비 계약을 정하며 추가 매개변수는 그 뒤에 둡니다.
2

메서드가 아닌 연관 함수

fn square · -> Self

Rectangle::square(3)

인스턴스 없이 타입 경로에서 호출합니다. Self는 대상 타입의 별칭이며, 생성 함수 이름 new는 관례일 뿐 키워드가 아닙니다.
3

여러 inherent impl

같은 타입에 블록을 둘 이상 정의

문법상 유효하며 모든 항목은 같은 타입 API에 합쳐집니다. 블록을 나눠도 호출 문법이나 네임스페이스가 달라지지 않습니다.
4

필드와 같은 이름의 메서드

rect.width · rect.width()

괄호 없이 쓰면 필드에 접근하고, 괄호를 붙이면 메서드를 호출합니다. 같은 이름을 써도 두 문법으로 구분됩니다.
메서드 · rect.area()
첫 매개변수 self가 기존 인스턴스를 받고 소유권 계약을 드러냅니다.
연관 함수 · Rectangle::square
self 없이 타입 경로로 호출합니다. 반환 타입과 생성식의 Self는 Rectangle을 뜻합니다. 생성 함수 이름 new는 관례이며 키워드가 아닙니다.
여러 impl · 하나의 API
블록을 나눠도 별도 타입이 생기지 않으며 호출 방식도 그대로입니다.
필드와 같은 이름도 가능
rect.width는 필드이고 rect.width()는 메서드입니다. 괄호가 둘을 구분합니다.
can_hold처럼 메서드는 self 뒤에 일반 함수와 같은 추가 매개변수를 둘 수 있습니다. 여러 impl은 필요할 때만 책임이나 제네릭 조건을 나누는 수단입니다.

여러 개의 impl 블록

각 구조체는 여러 개의 impl 블록을 가질 수 있습니다.

다음 예제 4-16은 예제 4-15에 나온 코드를 변경해 impl 블록을 여러 개로 만든 모습입니다.

예제 4-16: 예제 4-15를 여러 impl 블록을 사용하도록 재작성하기
impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

impl Rectangle {
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

위 코드에서는 impl 블록을 여러 개로 나눠야 할 이유가 전혀 없지만, impl 블록을 반드시 하나만 작성해야 할 필요는 없음을 보여드리기 위한 예시로 작성했습니다.

여러 impl 블록을 유용하게 사용하는 경우는 제네릭 타입 및 트레이트 내용을 다루는 9장에서 보실 수 있습니다.

블록을 나누더라도 별도의 타입이나 네임스페이스가 생기는 것은 아닙니다. 각 블록의 항목은 모두 같은 Rectangle API에 속하며 호출 문법도 달라지지 않습니다.

예시 다음은 self 세 형태와 인스턴스 없이 호출하는 square를 한 번씩 연결한 별도 완전한 프로그램입니다. 앞의 Rectangle 정의와 합치지 말고 새 파일로 판단합니다. enlarge의 곱셈은 이 실습의 작은 치수·배수만 사용합니다.

생성·읽기·변경·소비를 연결한 예
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn square(size: u32) -> Self {
        Self { width: size, height: size }
    }
    fn area(&self) -> u32 {
        self.width * self.height
    }
    fn enlarge(&mut self, factor: u32) {
        self.width *= factor;
        self.height *= factor;
    }
    fn into_dimensions(self) -> (u32, u32) {
        (self.width, self.height)
    }
}

fn main() {
    let mut rect = Rectangle::square(3);
    println!("{}", rect.area());
    rect.enlarge(2);
    println!("{}", rect.area());
    let dimensions = rect.into_dimensions();
    println!("{} {}", dimensions.0, dimensions.1);
}

예상 출력은 9, 36, 6 6입니다. square가 만든 3×3 인스턴스를 area는 공유 대여해 읽고, enlarge는 가변 대여해 6×6으로 바꿉니다. 두 호출 뒤에도 소유권은 main에 남습니다. 마지막 into_dimensions는 Copy가 아닌 Rectangle을 값으로 받아 소비하므로 결과 튜플은 사용할 수 있지만 원 rect는 다시 사용할 수 없습니다.

독립 변형 square의 인수를 4, enlarge의 배수를 3으로 바꾸면 세 출력은 무엇인가요? 첫 선언의 mut만 지우면 어디에서 실패할까요? into_dimensions 뒤에 rect.area()를 추가한 변형의 실패 이유도 가변성 문제와 구분하세요.

호출별 상태와 실패 조건의 풀이

4×4의 넓이 16, 12×12의 넓이 144, 치수 12 12입니다. mut가 없으면 enlarge에서 가변 대여할 수 없고, into_dimensions 뒤의 원 rect 재사용은 값이 이동되어 허용되지 않습니다. 읽기 메서드가 붙었다고 이미 이동한 인스턴스를 되살리지는 않습니다. 메서드의 수신자와 연관 함수, 점 호출의 탐색·호출 조건은 두 종류의 조건을 구분합니다.


핵심 포인트 정리

구조체를 사용하면 도메인에 의미 있는 커스텀 타입을 만들 수 있습니다.

또한, 구조체를 사용함으로써 서로 관련 있는 데이터들을 하나로 묶어 관리할 수 있으며, 각 데이터 조각에 이름을 붙여 코드를 더 명확하게 만들 수 있습니다.

impl 블록 내에서는 여러분의 타입에 대한 연관 함수들, 그리고 연관 함수의 일종인 메서드를 정의하여 여러분의 구조체 인스턴스가 가질 동작들을 명시할 수 있습니다.

하지만 구조체로만 커스텀 타입을 만들 수 있는 건 아닙니다.

다음에는 열거형을 배워서 여러분이 쓸 수 있는 도구를 하나 더 늘려보도록 합시다.

메서드와 연관 함수를 읽을 때는 self를 받는지, 인스턴스 없이 호출하는지, 여러 impl 블록이 어떤 책임으로 나뉘는지를 함께 보면 구조가 선명해집니다.