rtviii
rtviii

Reputation: 897

How does `.into()` know which type to convert to if `From` is implemented for multiple types?

How does the the <From> trait know which type to convert to in different contexts if it's implemented for more than one type?

For example, i have three types that have some mutual conversions implemented. On Character i implement From for both Token and AminoAcid. Yet, when i call .into() in the hashmap i don't have to specify which type is required by the map's type definition. How is the trait aware of the context?


#[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<&'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<&'static str, AminoAcid> = { // Here the value type is AminoAcid 
        let mut m = HashMap::new();
        m.insert("ALA", Character::Second.into());
        m
    };

}

Upvotes: 1

Views: 325

Answers (2)

prog-fh
prog-fh

Reputation: 16815

It's because Rust can deduce/infer types. For example

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 and b are declared the same way; at this point, the compiler just knows they are some kind of integer. At the first call of f1(), since the expected argument is an u8, Rust deduces that a is actually an u8. The same goes for b deduced as an i64 at the first usage of f2(). Of course, if after that we try to make the compiler deduce other types for these variables, it fails.

In your case, you declare your static bindings as hashmaps of a specific type: Token in one case, AminoAcid in the other. Then, the brace used to initialise such a binding have to match this type; the type of the resulting expression (m) is deduced accordingly. The way we initialise m expects the correct value type. Consequently, the version of into() providing this type is chosen.

Upvotes: 3

do-no-van
do-no-van

Reputation: 236

Because you defined the type of HASHMAP, the compiler infers which type is needed, since if it converted into the other type it would not compile. https://doc.rust-lang.org/rust-by-example/types/inference.html

Upvotes: 0

Related Questions