Error: mismatched types

Hi I'm pretty new to Rust.I was writing a macro these days and I get an error ,can someone explain why did the errors occur and how to fix it?Thanks.

code:
playground

/*
 * @Descripttion:
 * @version:
 * @Author: Caviar-X
 * @Date: 2021-06-18 22:05:38
 * @LastEditors: Please set LastEditors
 * @LastEditTime: 2021-06-24 11:57:56
 */

use std::io::stdin;
/*fn input(num : i32) -> Vec<i128> {//input function
    if num < 1 {
        panic!("num cannot smaller than 1!");
    }
    let mut inpstr = String::new();
    let mut result:Vec<i128> = Vec::new();
    stdin().read_line(&mut inpstr).expect("Failed to read");
    let strt : Vec<&str> = inpstr.split_whitespace().collect();
    for s in strt {
        result.push(s.parse::<i128>().unwrap());
    }
    result
}*/
fn is_digit(s: String) -> bool {
    let chars_are_numeric: Vec<bool> = s.chars().map(|c| c.is_numeric()).collect();
    !chars_are_numeric.contains(&false)
}
fn is_float(s: String) -> bool {
    s.contains(".")
        && is_digit(s[0..s.find(".").unwrap()].to_string())
        && is_digit(s[s.find(".").unwrap() + 1..s.len()].to_string())
}
macro_rules! read {
    ($(&mut $key:expr),*) => {
        let mut inpstr = String::new();
        let mut result:Vec<&str> = Vec::new();
        stdin()
            .read_line(&mut inpstr)
            .expect("Error: Failed to read.");
        result = inpstr.split_whitespace().collect();
        let mut i = 0;
        $(
        loop {
            if i == result.len()  {
                break;
            } else if is_digit(String::from(result[i])) {
                $key = result[i].parse::<i128>().unwrap();
            } else if is_float(result[i].to_string()) {
                $key = result[i].parse::<f64>().unwrap();
            }
            else {
                $key = result[i];
            }
            i+=1;
        }
    )*
    };
}
fn main() {
/*
    let mut a = 0;
    read!(&mut a);
    assert_eq!(a,5);
*/
}

Stanard Error:

error[E0308]: mismatched types
  --> src/main.rs:49:24
   |
49 |                 $key = result[i].parse::<f64>().unwrap();
   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i128`, found `f64`
...
61 |     read!(&mut a);
   |     -------------- in this macro invocation
   |
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0308]: mismatched types
  --> src/main.rs:52:24
   |
52 |                 $key = result[i];
   |                        ^^^^^^^^^ expected `i128`, found `&str`
...
61 |     read!(&mut a);
   |     -------------- in this macro invocation
   |
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0308`.
error: could not compile `OI`

To learn more, run the command again with --verbose.

By the way,I wrote this code in C ways,so It may not safe,if you have suggestions to my code,welcome to bring up it.

You're trying to assign values of different types to the same variable. The compiler first infers the type as i128 because that's the assignment it sees first, and it then errors when it sees you try to assign values of types f64 and &str to it later. The error is the same as in

let mut a = 0;
a = "some string";

If you want something that can be one of several types, you'll usually want an enum

enum MyEnum {
    Int(i128),
    Float(f64),
    String(String),
}

You would then assign to it like so:

$key = MyEnum::Int(result[i].parse::<i128>().unwrap());

As for general suggestions, is_digit and is_float can take a &str parameter and do the check using Iterator::any. In addition, the naming of is_digit is a little confusing because currently it will return true for "1234", which is not a digit. is_numeric might be better.

fn is_numeric(s: &str) -> bool {
    s.chars().any(char::is_numeric)
}

Rather than checking if the string consists of digits and then parsing it and unwrapping the result, you can just try to parse it and see if it works or not. With this, you don't need is_digit or is_float.

if let Ok(parsed) = s.parse::<i128>() {
    $key = parsed
} else if let Ok(parsed) = ...

Here, if parsing succeeds, you can use the parsed value inside the if-block, otherwise we continue like in a normal if-else.

Thank you!

Sorry,but I still don't get it.

I tried to fix it.But it failed again.

my code:

/*
 * @Descripttion:
 * @version:
 * @Author: Caviar-X
 * @Date: 2021-06-18 22:05:38
 * @LastEditors: Please set LastEditors
 * @LastEditTime: 2021-06-24 20:21:57
 */

use std::io::stdin;
/*fn input(num : i32) -> Vec<i128> {//input function
    if num < 1 {
        panic!("num cannot smaller than 1!");
    }
    let mut inpstr = String::new();
    let mut result:Vec<i128> = Vec::new();
    stdin().read_line(&mut inpstr).expect("Failed to read");
    let strt : Vec<&str> = inpstr.split_whitespace().collect();
    for s in strt {
        result.push(s.parse::<i128>().unwrap());
    }
    result
}*/
enum Ret {
    Int(i128),
    Float(f64),
    String(String),
}
/*fn is_numeric(s: String) -> bool {
    s.chars().any(char::is_numeric)
}
fn is_float(s: String) -> bool {
    s.contains(".")
        && is_numeric(s[0..s.find(".").unwrap()].to_string())
        && is_numeric(s[s.find(".").unwrap() + 1..s.len()].to_string())
}*/
macro_rules! read {
    ($(&mut $key:expr),*) => {
        let mut inpstr = String::new();
        let mut result:Vec<&str> = Vec::new();
        stdin()
            .read_line(&mut inpstr)
            .expect("Error: Failed to read.");
        result = inpstr.split_whitespace().collect();
        let mut i = 0;
        $(
        loop {
            if i == result.len()  {
                break;
            }
            if let Ok(parse1) = result[i].parse::<i128>() {
                $key = Ret::Int(parse1);
            } else if let Ok(parse2) = result[i].parse::<f64>() {
                $key = Ret::Float(parse2);
            } else {
                $key = Ret::String(result[i]);
            }
            i+=1;
        }
    )*
    };
}
fn main() {}

I'm very sorry about disturbing you.

You can change the enum to contain a &str instead of String

enum Ret<'a> {
    Int(i128),
    Float(f64),
    String(&'a str),
}

or you can turn result[i] into a String

$key = Ret::String(result[i].to_string());

By the way, you can use the same variable name in both if let Ok(...) bits and you can drop the type annotations from parse(), the compiler can infer the correct type from how they are used with the Ret enum.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.