메서드 호출 표현식(Method-call expressions)

메서드 호출 표현식(Method-call expressions)

메서드 호출은 어떤 값을 "수신자(receiver)"로 두고 그 메서드를 부르는 방식이에요. 문법은 익숙하죠. 표현식 다음에 점 하나, 그다음 경로 세그먼트, 그리고 괄호로 묶인 인자 목록으로 이뤄져요.

MethodCallExpression → Expression . PathExprSegment ( CallParams? )

메서드 호출은 특정 트레잇에 정의된 연관 메서드(associated method)로 해석돼요. 왼쪽 피연산자의 정확한 self 타입을 알 수 있으면 정적으로 디스패치하고, 왼쪽이 간접적인 트레잇 객체라면 동적으로 디스패치하죠.

#![allow(unused)]
fn main() {
let pi: Result<f32, _> = "3.14".parse();
let log_pi = pi.unwrap_or(1.0).log(2.72);
assert!(1.14 < log_pi && log_pi < 1.15)
}

출처: Rust Reference

본문

메서드 찾는 과정(자동 역참조와 빌림)

메서드 호출을 찾을 땐 수신자를 자동으로 역참조하거나 빌려서 메서드를 부르기도 해요. 그래서 다른 함수보다 더 복잡한 탐색 과정을 거쳐요. 가능한 메서드가 여러 개일 수 있기 때문이죠. 과정을 따라가 볼게요.

먼저 후보 수신자 타입 목록을 만들어요. 수신자 표현식의 타입을 반복적으로 역참조하면서 만나는 타입을 목록에 추가하고, 마지막에는 배열 크기 미정(unsized) 강제 변환을 시도해 그 결과 타입까지 추가해요. 그다음 각 후보 T에 대해 바로 뒤에 &T&mut T를 추가해요.

예를 들어 수신자 타입이 Box<[i32;2]>라면 후보 타입은 이렇게 돼요.

Box<[i32;2]>, &Box<[i32;2]>, &mut Box<[i32;2]>,   // 역참조 전
[i32; 2], &[i32; 2], &mut [i32; 2],               // 역참조 후
[i32], &[i32], &mut [i32]                         // unsized 강제 변환 후

그다음 각 후보 타입 T에 대해 그 타입을 수신자로 갖는 보이는(visible) 메서드를 다음 위치에서 찾아요.

  • T의 고유(inherent) 메서드 — T에 직접 구현된 메서드
  • T가 구현한 보이는 트레잇이 제공하는 메서드. T가 타입 파라미터라면 T의 트레잇 바운드가 제공하는 메서드를 먼저 찾고, 그다음 스코프에 남은 모든 메서드를 찾아요.

검색은 각 타입을 순서대로 수행하기 때문에 가끔 의외의 결과가 나올 수 있어요. 아래 코드는 "In trait impl!"을 출력해요. &self 메서드가 먼저 조회되니까, 구조체의 &mut self 메서드보다 트레잇 메서드가 먼저 발견되거든요.

struct Foo {}

trait Bar {
  fn bar(&self);
}

impl Foo {
  fn bar(&mut self) {
    println!("In struct impl!")
  }
}

impl Bar for Foo {
  fn bar(&self) {
    println!("In trait impl!")
  }
}

fn main() {
  let mut f = Foo{};
  f.bar();
}

만약 후보가 여러 개 나오면 오류예요. 그럴 때는 수신자를 적절한 수신자 타입으로 변환해서 메서드 호출을 명확히 해야 해요.

이 탐색 과정은 수신자의 가변성(mutability)이나 수명, 메서드가 unsafe인지 여부는 고려하지 않아요. 메서드를 찾은 뒤 그런 이유로 호출할 수 없다면 컴파일 오류가 나죠.

또 한 단계에서 제네릭 메서드나 트레잇이 동등하게 여겨져 후보가 둘 이상 나오는 경우에도 컴파일 오류예요. 이럴 땐 모호함을 없애는 함수 호출 구문(disambiguating function call syntax)을 써서 메서드나 함수를 호출해야 해요.

2021 에디션 차이

2021 에디션 이전에는 보이는 메서드 탐색 중 후보 수신자 타입이 배열 타입이면 표준 라이브러리 IntoIterator 트레잇이 제공하는 메서드를 무시했어요. 이 판단에 쓰는 에디션은 메서드 이름을 나타내는 토큰으로 정해요. 이 특별 규칙은 앞으로 제거될 수도 있어요.

경고

트레잇 객체에 트레잇 메서드와 같은 이름의 고유 메서드가 있으면, 메서드 호출 표현식에서 그 메서드를 부르려 할 때 컴파일 오류가 나요. 이때는 모호함을 없애는 함수 호출 구문으로 부를 수 있는데, 그러면 고유 메서드가 아니라 트레잇 메서드가 호출돼요. 고유 메서드를 부를 방법은 없어요. 그러니 트레잇 객체에 트레잇 메서드와 같은 이름의 고유 메서드를 정의만 하지 않으면 돼요.

더 알아보기

  • 연관 아이템: 메서드가 정의되는 곳
  • 함수 호출 구문과 모호성 해소
  • 포인터 연산자와 역참조
  • 정적/동적 디스패치