나는 Rust와 함께 배우고 / 실험하고 있으며,이 언어에서 발견되는 모든 우아함에 나를 방해하고 완전히 벗어난 것처럼 보이는 하나의 특이성이 있습니다.
Rust는 메소드 호출시 포인터를 자동으로 역 참조합니다. 정확한 동작을 결정하기 위해 몇 가지 테스트를 수행했습니다.
struct X { val: i32 }
impl std::ops::Deref for X {
type Target = i32;
fn deref(&self) -> &i32 { &self.val }
}
trait M { fn m(self); }
impl M for i32 { fn m(self) { println!("i32::m()"); } }
impl M for X { fn m(self) { println!("X::m()"); } }
impl M for &X { fn m(self) { println!("&X::m()"); } }
impl M for &&X { fn m(self) { println!("&&X::m()"); } }
impl M for &&&X { fn m(self) { println!("&&&X::m()"); } }
trait RefM { fn refm(&self); }
impl RefM for i32 { fn refm(&self) { println!("i32::refm()"); } }
impl RefM for X { fn refm(&self) { println!("X::refm()"); } }
impl RefM for &X { fn refm(&self) { println!("&X::refm()"); } }
impl RefM for &&X { fn refm(&self) { println!("&&X::refm()"); } }
impl RefM for &&&X { fn refm(&self) { println!("&&&X::refm()"); } }
struct Y { val: i32 }
impl std::ops::Deref for Y {
type Target = i32;
fn deref(&self) -> &i32 { &self.val }
}
struct Z { val: Y }
impl std::ops::Deref for Z {
type Target = Y;
fn deref(&self) -> &Y { &self.val }
}
#[derive(Clone, Copy)]
struct A;
impl M for A { fn m(self) { println!("A::m()"); } }
impl M for &&&A { fn m(self) { println!("&&&A::m()"); } }
impl RefM for A { fn refm(&self) { println!("A::refm()"); } }
impl RefM for &&&A { fn refm(&self) { println!("&&&A::refm()"); } }
fn main() {
// I'll use @ to denote left side of the dot operator
(*X{val:42}).m(); // i32::m() , Self == @
X{val:42}.m(); // X::m() , Self == @
(&X{val:42}).m(); // &X::m() , Self == @
(&&X{val:42}).m(); // &&X::m() , Self == @
(&&&X{val:42}).m(); // &&&X:m() , Self == @
(&&&&X{val:42}).m(); // &&&X::m() , Self == *@
(&&&&&X{val:42}).m(); // &&&X::m() , Self == **@
println!("-------------------------");
(*X{val:42}).refm(); // i32::refm() , Self == @
X{val:42}.refm(); // X::refm() , Self == @
(&X{val:42}).refm(); // X::refm() , Self == *@
(&&X{val:42}).refm(); // &X::refm() , Self == *@
(&&&X{val:42}).refm(); // &&X::refm() , Self == *@
(&&&&X{val:42}).refm(); // &&&X::refm(), Self == *@
(&&&&&X{val:42}).refm(); // &&&X::refm(), Self == **@
println!("-------------------------");
Y{val:42}.refm(); // i32::refm() , Self == *@
Z{val:Y{val:42}}.refm(); // i32::refm() , Self == **@
println!("-------------------------");
A.m(); // A::m() , Self == @
// without the Copy trait, (&A).m() would be a compilation error:
// cannot move out of borrowed content
(&A).m(); // A::m() , Self == *@
(&&A).m(); // &&&A::m() , Self == &@
(&&&A).m(); // &&&A::m() , Self == @
A.refm(); // A::refm() , Self == @
(&A).refm(); // A::refm() , Self == *@
(&&A).refm(); // A::refm() , Self == **@
(&&&A).refm(); // &&&A::refm(), Self == @
}
( 운동장 )
따라서 다소간 나타납니다.
- 컴파일러는 메소드 호출에 필요한만큼의 역 참조 연산자를 삽입합니다.
&self
(참조 별 호출)을 사용하여 선언 된 메소드를 해석 할 때 컴파일러는 다음을 수행합니다.- 먼저 하나의 역 참조를 요구합니다.
self
- 그런 다음 정확한 유형의 전화를 시도합니다
self
- 그런 다음 일치하는 데 필요한만큼의 역 참조 연산자를 삽입합니다.
- 먼저 하나의 역 참조를 요구합니다.
self
유형에 대해 (값별 호출)을 사용하여 선언 된 메소드는 유형에 대해 (기준 별 호출)을T
사용하여 선언 되고 도트 연산자의 왼쪽에있는 모든 것에 대한 참조에서 호출 된 것처럼 작동합니다 .&self
&T
- 위의 규칙은 먼저 내장 된 역 참조로 시도되며, 일치하지 않으면
Deref
특성이 있는 과부하 가 사용됩니다.
정확한 자동 역 참조 규칙은 무엇입니까? 누구든지 그러한 디자인 결정에 대한 공식적인 근거를 줄 수 있습니까?