Это действительно сбивающее с толку сообщение об ошибке, и причина, по которой вы его получаете, довольно тонкая.Ответ от ozkriff правильно объясняет, что это потому, что черта Read
не находится в области видимости.Я хотел бы добавить немного больше контекста и объяснение, почему вы получаете конкретную ошибку, которую вы видите, а не ошибку, что метод не найден.
Метод take()
в Read
и Iterator
принимает self
по значению, или, другими словами, он потребляет свой получатель.Это означает, что вы можете позвонить, только если у вас есть право собственности на получателя.Функции в вашем вопросе принимают iter
по изменяемой ссылке, поэтому они не владеют базовым I
объектом, поэтому вы не можете вызвать <Iterator>::take()
или <Read>::take()
для базового объекта.
Однако, как указал ozkriff, стандартная библиотека предоставляет реализации «пересылки» Iterator
и Read
для изменяемых ссылок на типы, которые реализуют соответствующие признаки.Когда вы вызываете iter.take(2)
в своей первой функции, вы фактически заканчиваете тем, что вызываете <&mut Iterator<Item = T>>::take(iter, 2)
, который использует только вашу изменяемую ссылку на итератор, а не сам итератор.Это совершенно верно;в то время как функция не может использовать сам итератор, поскольку ей не принадлежит эта ссылка, функция владеет ссылкой.Однако во второй функции вы в итоге вызываете <Read>::take(*iter, 2)
, который пытается использовать базовый читатель.Поскольку у вас нет этого читателя, вы получаете сообщение об ошибке, объясняющее, что вы не можете переместить его из заимствованного контекста.
Так почему второй вызов метода разрешает другой метод?Ответ Озкриффа уже объясняет, что это происходит потому, что черта Iterator
входит в стандартную прелюдию, а черта Read
по умолчанию не входит в сферу.Давайте посмотрим на метод поиска более подробно.Это описано в разделе «Выражения вызова метода» справочника по языку Rust:
Первым шагом является создание списка возможных типов получателей.Получите их, многократно разыменовывая тип получающего выражения, добавляя каждый встреченный тип в список, затем, в конце, пытайтесь выполнить принудительное приведение в конце и добавьте тип результата, если это успешно.Затем для каждого кандидата T
добавьте &T
и &mut T
в список сразу после T
.
Согласно этому правилу наш список типов кандидатов будет
&mut I, &&mut I, &mut &mut I, I, &I, &mut I
Затем для каждого типа кандидата T
найдите видимый метод с приемником этого типа в следующих местах:
T
's встроенные методы (методы, реализованные непосредственно в T
).
Любой из методов, предоставляемых видимой чертой, реализованной T
.Если T
является параметром типа, сначала ищутся методы, предоставляемые границами признаков для T
.Затем ищутся все остальные методы в области видимости.
Для случая I: Iterator
этот процесс начинается с поиска метода take()
в &mut I
.Для &mut I
нет внутренних методов, поскольку I
является универсальным типом, поэтому мы можем пропустить шаг 1. На шаге 2 мы сначала ищем методы на границах признаков для &mut I
, но есть только границы признаков дляI
, поэтому мы переходим к поиску take()
для всех оставшихся методов в области видимости.Поскольку Iterator
находится в области действия, мы действительно находим реализацию перенаправления из стандартной библиотеки и можем прекратить обработку нашего списка типов-кандидатов.
Для второго случая, I: Read
, мы также начинаем с &mut I
, но поскольку Read
находится вне области действия, мы не увидим реализацию пересылки.Однако, как только мы доберемся до I
в нашем списке типов кандидатов, включается предложение о методах, предоставляемых границами признаков: они сначала ищутся, независимо от того, находится ли свойство в области видимости.I
имеет границу черты Read
, поэтому <Read>::take()
найдено.Как мы видели выше, вызов этого метода приводит к сообщению об ошибке.
Таким образом, черты должны находиться в области видимости, чтобы использовать их методы, но методы на границах признаков могут использоваться, даже если черта не находится в области видимости..