Во-первых, нам нужно понять, что такое deref coercion. Если T
заменяется на U
и x
является значением типа T
, то:
*x
равно *Deref::deref(&x)
&T
может быть приведенным к &U
x.method()
будет проверять тип U
во время разрешения метода.
Как работает разрешение метода, когда вы вызываете метод для типа, он сначала проверяет метод, ничего не добавляя к типу, затем добавляя &
, затем добавляя &mut
, а затем разыменовывая. Итак, когда выясняется, какой метод вызвать для x.method()
, он сначала проверит метод, который принимает T
, затем &T
, затем &mut T
, затем U
, затем &U
, затем &mut U
( подробнее здесь ). Этот не применяется к операторам. Следовательно, ==
не будет приводить различные типы, поэтому вы должны явно разыменовать.
Но что, если бы мы действительно использовали метод, например .eq
в трейте PartialEq
? Все становится интересно. Следующий код не работает:
fn get_last(list: &List) -> &List {
match list {
Nil => list,
Cons(_, next_list) if next_list.eq(Nil) => list,
Cons(_, next_list) => get_last(next_list),
}
}
, но следующий успешно:
fn get_last(list: &List) -> &List {
match list {
Nil => list,
// notice how it's Nil.eq and not next_list.eq
Cons(_, next_list) if Nil.eq(next_list) => list,
Cons(_, next_list) => get_last(next_list),
}
}
Почему это? Давайте посмотрим на первый пример:
next_list
имеет тип &Rc<List>
, поэтому он начинает поиск метода .eq
. Он немедленно находит определенную в реализации PartialEq
для Rc
с подписью fn eq(&self, other: &Rc<List>)
. Однако в данном случае other
имеет тип List
, который не может быть приведен к &Rc<List>
.
Тогда почему работает второй? Nil
имеет тип List
, поэтому начинает поиск метода .eq
. Он не может найти ничего для List
, поэтому затем пытается &List
, где находит производную имплантацию PartialEq
с подписью fn eq(&self, other: &List)
. В этом случае other имеет тип &Rc<List>
, который может быть приведен к &List
из-за его реализации Deref
. Это означает, что все типы проверяются правильно, и код работает. назад в 2017 .