Ответ 1
Ваш псевдокод в значительной степени верен. Для этого примера предположим, что у нас был вызов метода foo.bar()
где foo: T
. Я использую полный синтаксис (FQS), чтобы быть однозначным в отношении того, с каким типом метода вызывается, например. A::bar(foo)
или A::bar(&***foo)
. Я просто собираюсь написать кучу случайных заглавных букв, каждый из которых является лишь некоторым произвольным типом/признаком, за исключением того, что T
всегда является типом исходной переменной foo
, вызываемой методом.
Ядро алгоритма:
- Для каждого "шаг разворота"
U
(то есть установитеU = T
, а затемU = *T
,...)- если существует метод
bar
, где тип приемника (типself
в методе) точно соответствуетU
, используйте его (a "по методу значений" ) - в противном случае добавьте один авто-ref (возьмите
&
или&mut
получателя), и если какой-либо приемник метода соответствует&U
, используйте его ( "метод autorefd" )
- если существует метод
Примечательно, что все рассматривает "тип приемника" метода, а не тип self
признака, т.е. impl ... for Foo { fn method(&self) {} }
думает о &Foo
при сопоставлении метода, а fn method2(&mut self)
будет думать о &mut Foo
при сопоставлении.
Это ошибка, если на внутренних этапах есть допустимые методы с несколькими признаками (то есть, могут быть только нулевые или одни методы признака, действительные в каждом из 1. или 2., но может быть один действительный для каждого: первый из 1 будет сделан первым), а присущие ему методы имеют приоритет над чертами. Это также ошибка, если мы дойдем до конца цикла, не найдя ничего, что соответствует. Это также ошибка для рекурсивных реализаций Deref
, которые делают цикл бесконечным (они попадут в "предел рекурсии" ).
Эти правила, по-видимому, делают-что-я-значение в большинстве случаев, хотя наличие возможности писать однозначную форму FQS очень полезно в некоторых случаях кросс-кода и для разумных сообщений об ошибках для макрогенерированного кода.
Добавлена только одна автоматическая ссылка, потому что
- Если не было никакой привязки, все становится плохо/медленным, так как каждый тип может иметь произвольное количество принятых ссылок
- взятие одной ссылки
&Foo
сохраняет сильное соединение сfoo
(это адрес самогоfoo
), но при этом больше начинает его потерять:&&foo
- это адрес некоторой временной переменной в стеке который хранит&Foo
.
Примеры
Предположим, что у нас есть вызов foo.refm()
, если foo
имеет тип:
-
X
, тогда мы начинаем сU = X
,refm
имеет тип приемника&...
, поэтому шаг 1 не соответствует, взяв auto-ref, дает нам&X
, и это соответствует (сSelf = X
), поэтому вызовRefM::refm(&foo)
-
&X
начинается сU = &X
, который соответствует&self
на первом шаге (сSelf = X
), и поэтому вызовRefM::refm(foo)
-
&&&&&X
, это не соответствует ни шагу (черта не реализована для&&&&X
или&&&&&X
), поэтому мы разыскиваем один раз, чтобы получитьU = &&&&X
, который соответствует 1 (сSelf = &&&X
) и вызовRefM::refm(*foo)
-
Z
, не соответствует ни одному из шагов, поэтому разыменован один раз, чтобы получитьY
, который также не соответствует, поэтому он разыменован снова, чтобы получитьX
, который не соответствует 1, но соответствует автореффиксу, поэтому вызовRefM::refm(&**foo)
. -
&&A
, 1. не совпадает и не имеет значения 2. поскольку черта не реализована для&A
(для 1) или&&A
(для 2), поэтому она разыменована на&A
, который соответствует 1., сSelf = A
Предположим, что мы имеем foo.m()
и что A
не Copy
, если foo
имеет тип:
-
A
, тогдаU = A
соответствуетself
напрямую, поэтому вызовM::m(foo)
сSelf = A
-
&A
, то 1. не соответствует, и не делает 2. (ни&A
, ни&&A
не реализует этот признак), поэтому он разыменован наA
, который соответствует, ноM::m(*foo)
требует принятияA
по значению и, следовательно, выхода изfoo
, следовательно, ошибки. -
&&A
, 1. не соответствует, но autorefing дает&&&A
, который соответствует, поэтому вызовM::m(&foo)
сSelf = &&&A
.
(Этот ответ основан на код, а достаточно близко к (слегка устаревшему) README. Нико Мацакис, главный автор этой части компилятора/языка, также просмотрел этот ответ.)