Метод Vec::<T>::get()
возвращает Option<&T>
, то есть опцию ссылки на вектор. Поскольку ваш вектор уже содержит ссылки (&str
), это означает, что get()
в вашем случае возвращает Option<&&str>
.
Итак, причина, по которой ваш первый пример компилируется, заключается в том, что, явно указав тип переменной s
, вы вызываете разыменование coercion , то есть компилятор автоматически вставляет оператор разыменования :
// You write this:
let s: &str = v.get(0).unwrap();
// Compiler actually does this:
let s: &str = *v.get(0).unwrap();
Тогда, поскольку существует реализация признака From<&str>
для String
, компилятор принимает вызов String::from(s)
.
Однако во втором фрагменте без явной аннотации типа тип, назначенный для s
, равен &&str
:
let s: &&str = v.get(0).unwrap();
Это меняет ситуацию с методом String::from()
: для String
нет реализации From<&&str>
, и компилятор не может скомпилировать код.
Логичный вопрос здесь заключается в том, почему в этом случае не происходит разглашение, когда в качестве аргумента для from()
используется s
? Ответ заключается в том, что принудительное приведение не происходит для универсальных методов, и String::from
является универсальным методом, потому что он опирается на параметр типа признака From
. Компилятор не может выполнить принудительное приведение, потому что вполне возможно (в принципе) иметь методы From<&str>
и From<&&str>
, которые потенциально могут выполнять разные действия.
Даже если From<&&str>
сейчас не существует, и компилятор применил бы принудительное приведение, это сделало бы код хрупким по отношению к будущей эволюции: если по какой-то причине From<&&str>
будет добавлено в будущую версию стандартной библиотеки ваш код может молча сломаться, потому что теперь он будет вызывать другой метод, чем раньше, с потенциально другой логикой.
(Естественно, я не говорю, что From<&&str>
действительно будет добавлено в будущую версию libstd; это общие рассуждения, применимые к любому типу признаков, в любой кодовой базе).