메서드 호출 표현식(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트레잇이 제공하는 메서드를 무시했어요. 이 판단에 쓰는 에디션은 메서드 이름을 나타내는 토큰으로 정해요. 이 특별 규칙은 앞으로 제거될 수도 있어요.
경고
트레잇 객체에 트레잇 메서드와 같은 이름의 고유 메서드가 있으면, 메서드 호출 표현식에서 그 메서드를 부르려 할 때 컴파일 오류가 나요. 이때는 모호함을 없애는 함수 호출 구문으로 부를 수 있는데, 그러면 고유 메서드가 아니라 트레잇 메서드가 호출돼요. 고유 메서드를 부를 방법은 없어요. 그러니 트레잇 객체에 트레잇 메서드와 같은 이름의 고유 메서드를 정의만 하지 않으면 돼요.
더 알아보기
- 연관 아이템: 메서드가 정의되는 곳
- 함수 호출 구문과 모호성 해소
- 포인터 연산자와 역참조
- 정적/동적 디스패치