Как `.into()` узнает, в какой тип преобразовать, если `From` реализован для нескольких типов?

#rust

#Ржавчина

Вопрос:

Как <From> признак узнает, в какой тип преобразовать в разных контекстах, если он реализован для более чем одного типа?

Например, у меня есть три типа, в которых реализованы некоторые взаимные преобразования. Character Я реализую From для обоих Token и AminoAcid . Тем не менее, когда я вызываю .into() hashmap, мне не нужно указывать, какой тип требуется в определении типа карты. Как признак осознает контекст?

 
#[derive(Debug)]
struct Coordinate{
    x:f64,    y:f64,    z:f64
}
#[derive(Debug)]
enum AminoAcid {
    VAL, GLN ,ARG,...
}

#[derive(Debug)]
enum Token {
    Coordinate(Coordinate),
    AminoAcid(AminoAcid),
    Character(Character)
}


impl From<Coordinate> for Token  {
    fn from(coord: Coordinate) -> Self {
        Self::Coordinate(coord)
    }
}
impl From<AminoAcid> for Token  {
    fn from(aa: AminoAcid) -> Self {
        Self::AminoAcid(aa)
    }
}
// ------------------------------------------------- <<<<
impl From<Character> for Token  {
    fn from(c: Character) -> Self {
       Self::Character(c)
    }
}
impl From<Character> for AminoAcid  {
    fn from(c: Character) -> Self {
        Self::ALA
    }
}

// ------------------------------------------------- <<<<

lazy_static! {
    static ref HASHMAP: HashMap<amp;'static str, Token> = { // Here the value type is Token 
        let mut m = HashMap::new();
        m.insert("ALA", Character::Second.into());
        m
    };
    
  static ref HASHMAP: HashMap<amp;'static str, AminoAcid> = { // Here the value type is AminoAcid 
        let mut m = HashMap::new();
        m.insert("ALA", Character::Second.into());
        m
    };

}

 

Комментарии:

1. Я далеко не эксперт по Rust, но я почти уверен, что это из-за вывода типов — .into() вызовы здесь должны возвращать типы, которые они делают, потому что в противном insert случае вызовы не будут правильно типизированы, учитывая типы в HashMap, которые вы явно указали.

Ответ №1:

Это потому, что Rust может выводить / выводить типы. Например

 fn f1(arg: u8) {
    println!("u8 in f1: {}", arg)
}

fn f2(arg: i64) {
    println!("i64 in f2: {}", arg)
}

fn main() {
    let a = 12;
    let b = 23;
    f1(a);
    f2(b);
    // f1(b); // expected `u8`, found `i64`
    // f2(a); // expected `i64`, found `u8`
}
 

a и b объявляются одинаково; на этом этапе компилятор просто знает, что они представляют собой какое-то целое число.
При первом вызове f1() , поскольку ожидаемым аргументом является an u8 , Rust выводит, что a на самом деле это an u8 .
То же самое относится и к b выведенному как i64 при первом использовании f2() .
Конечно, если после этого мы попытаемся заставить компилятор вывести другие типы для этих переменных, это не удастся.

В вашем случае вы объявляете свои статические привязки как хэш-карты определенного типа: Token в одном случае, AminoAcid в другом. Затем фигурная скобка, используемая для инициализации такой привязки, должна соответствовать этому типу; соответственно выводится тип результирующего выражения ( m ) . Способ инициализации m предполагает правильный тип значения. Следовательно, выбирается версия into() предоставления этого типа.

Комментарии:

1. Это абсолютно вывод типа. Я должен также добавить, что это не всегда будет возможно, если окружающий код не предоставил механизму вывода типов достаточно информации, и в этом случае вы увидите ошибку компилятора с просьбой уточнить (либо путем добавления дополнительных подсказок типа, либо полного синтаксиса функции, который указывает, какую Into черту вы хотитедля использования)

Ответ №2:

Поскольку вы определили тип HASHMAP , компилятор определяет, какой тип необходим, поскольку, если он преобразуется в другой тип, он не будет компилироваться. https://doc.rust-lang.org/rust-by-example/types/inference.html