为什么无法解析字符串锈

时间:2019-05-26 17:58:59

标签: parsing types rust

我正在制作从十进制到二进制的转换程序。为什么我无法将字符串解析为数字? binary_vec[i]character。我使用to_string()方法将其转换为字符串,因为parse()不适用于字符,但仍然给我一个错误。

use std::io;

fn main() {
    let mut binary = String::new();
    println!("Enter a decimal: ");
    io::stdin().read_line(&mut binary)
        .ok()
        .expect("Couldn't read line");
    println!("{}",to_decimal(binary));

}

fn to_decimal(mut binary_str:String) -> String {
    let mut binary_no: u32 = binary_str.trim().parse().expect("invalid input");
    if binary_no == 0 {
        format!("{}",binary_no)
                } 
    else {
        let mut bits = String::new();
        let mut binary_vec: Vec<char> = binary_str.chars().collect();
        let mut result = 0;
        let mut i = 0;
        while i <=binary_str.len()-2{
            let mut result = result + (binary_vec[i].to_string().parse::<u32>().unwrap() * 2^(i as u32));
            i = i +1;
        }
        format!("{}",result)
        }
}

结果:

 Compiling sixteen v0.1.0 (C:\Users\Muhammad.3992348\Desktop\rust\hackathon\sixteen)
warning: unused variable: `bits`
  --> src\main.rs:19:17
   |
19 |         let mut bits = String::new();
   |                 ^^^^ help: consider prefixing with an underscore: `_bits`
   |
   = note: #[warn(unused_variables)] on by default

warning: unused variable: `result`
  --> src\main.rs:24:21
   |
24 |             let mut result = result + (binary_vec[i].to_string().parse::<u32>().unwrap() * 2^(i as u32));
   |                     ^^^^^^ help: consider prefixing with an underscore: `_result`

warning: variable does not need to be mutable
  --> src\main.rs:13:15
   |
13 | fn to_decimal(mut binary_str:String) -> String {
   |               ----^^^^^^^^^^
   |               |
   |               help: remove this `mut`
   |
   = note: #[warn(unused_mut)] on by default

warning: variable does not need to be mutable
  --> src\main.rs:14:9
   |
14 |     let mut binary_no: u32 = binary_str.trim().parse().expect("invalid input");
   |         ----^^^^^^^^^
   |         |
   |         help: remove this `mut`

warning: variable does not need to be mutable
  --> src\main.rs:19:13
   |
19 |         let mut bits = String::new();
   |             ----^^^^
   |             |
   |             help: remove this `mut`

warning: variable does not need to be mutable
  --> src\main.rs:20:13
   |
20 |         let mut binary_vec: Vec<char> = binary_str.chars().collect();
   |             ----^^^^^^^^^^
   |             |
   |             help: remove this `mut`

warning: variable does not need to be mutable
  --> src\main.rs:21:13
   |
21 |         let mut result = 0;
   |             ----^^^^^^
   |             |
   |             help: remove this `mut`

warning: variable does not need to be mutable
  --> src\main.rs:24:17
   |
24 |             let mut result = result + (binary_vec[i].to_string().parse::<u32>().unwrap() * 2^(i as u32));
   |                 ----^^^^^^
   |                 |
   |                 help: remove this `mut`

    Finished dev [unoptimized + debuginfo] target(s) in 1.04s
     Running `target\debug\sixteen.exe`
Enter a decimal:
100
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }', src\libcore\result.rs:997:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
error: process didn't exit successfully: `target\debug\sixteen.exe` (exit code: 101)

编辑: 我已经输入了100,我希望输出为4。

2 个答案:

答案 0 :(得分:8)

编写的代码有很多问题。好消息是,您可以通过听编译器警告来解决大多数问题!

  --> src/main.rs:19:17
   |
19 |         let mut bits = String::new();
   |                 ^^^^ help: consider prefixing with an underscore: `_bits`
   |
   = note: #[warn(unused_variables)] on by default

这告诉您在此声明之后未使用变量bits。如果您打算以后使用它,则可以暂时将其注释掉。

warning: unused variable: `result`
  --> src/main.rs:24:21
   |
24 |             let mut result = result + (binary_vec[i].to_string().parse::<u32>().unwrap() * 2^(i as u32));
   |                     ^^^^^^ help: consider prefixing with an underscore: `_result`
   |
   = note: #[warn(unused_variables)] on by default

这是大问题。这告诉我们变量result未使用。可是等等!我们在这里使用它!不!实际上,通过在此处使用let mut,我们可以创建一个新变量并覆盖旧变量。您想要的是改写旧值。只需将let mut result = ...更改为result = ...

现在,如果再次运行该程序并输入100,我们将得到5的输出。这似乎仍然不正确,但是我们还有一些警告需要解决,因此让我们回到这一点。

warning: variable does not need to be mutable
  --> src/main.rs:13:15
   |
13 | fn to_decimal(mut binary_str:String) -> String {
   |               ----^^^^^^^^^^
   |               |
   |               help: remove this `mut`
   |
   = note: #[warn(unused_mut)] on by default

如果我们不打算对输入字符串进行突变,则不应使其可变。只需删除mut。对于其他两个警告(第14行和第20行)也是如此。

好的!现在,我们可以在没有任何警告的情况下运行该程序。但是,我们可以使用cargo clippy运行一些更高级的棉绒。如果尚未安装clippy,则可以使用rustup install component clippy安装。

现在,我们还有其他警告要注意。

warning: operator precedence can trip the unwary
  --> src/main.rs:24:32
   |
24 |             result = result + (binary_vec[i].to_string().parse::<u32>().unwrap() * 2^(i as u32));
   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(binary_vec[i].to_string().parse::<u32>().unwrap() * 2) ^ (i as u32)`
   |
   = note: #[warn(clippy::precedence)] on by default
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#precedence

哦,哦。 Clippy告诉我们优先级规则表明*^之前被求值。这不是我们对乘法和指数的期望。事实证明^不是指数运算符。相反,它是按位异或。如果需要数字的幂,可以使用pow方法,因此将2 ^ (i as u32)替换为2.pow(i as u32)。这将导致编译器错误并显示消息error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}`。好。我们可以使用后缀使数字类型明确。将其更改为2_u32.pow(i as u32)

仅通过使用建议,我们就可以解决Clippy给我们的其他三个警告。之后,Clippy会再发出一条警告,该警告也仅通过建议解决。

在继续之前,让我们通过运行cargo fmt使代码更漂亮。最后,代码如下所示:

use std::io;

fn main() {
    let mut binary = String::new();
    println!("Enter a decimal: ");
    io::stdin()
        .read_line(&mut binary)
        .expect("Couldn't read line");
    println!("{}", to_decimal(binary));
}

fn to_decimal(binary_str: String) -> String {
    let binary_no: u32 = binary_str.trim().parse().expect("invalid input");
    if binary_no == 0 {
        format!("{}", binary_no)
    } else {
        //let mut bits = String::new();
        let binary_vec: Vec<char> = binary_str.chars().collect();
        let mut result = 0;
        let mut i = 0;
        while i <= binary_str.len() - 2 {
            result += binary_vec[i].to_string().parse::<u32>().unwrap() * 2_u32.pow(i as u32);
            i += 1;
        }
        format!("{}", result)
    }
}

我们修复了两个错误,仅使用编译器警告和基本工具来清理代码!还不错吧?但是现在,如果我们输入100,我们的输出就是1。那不对让我们添加一些调试语句,看看是否可以看到正在发生的事情。

result += dbg!(binary_vec[i].to_string().parse::<u32>().unwrap()) * dbg!(2_u32.pow(i as u32));

如果我们现在运行它,这就是我们得到的。

Enter a decimal: 
100
[src/main.rs:22] binary_vec[i].to_string().parse::<u32>().unwrap() = 1
[src/main.rs:22] 2u32.pow(i as u32) = 1
[src/main.rs:22] binary_vec[i].to_string().parse::<u32>().unwrap() = 0
[src/main.rs:22] 2u32.pow(i as u32) = 2
[src/main.rs:22] binary_vec[i].to_string().parse::<u32>().unwrap() = 0
[src/main.rs:22] 2u32.pow(i as u32) = 4
1

我们可以看到它正确地解析了数字:binary_vec[i].to_string().parse::<u32>().unwrap()1,然后是0,然后是0。 2的幂也很好。这里的问题是它倒退了!我们希望左边的数字乘以2的最高幂。让我注意,这是代码中您习惯最少的部分。至少要使用for循环来处理这种事情要好得多。如果您可以管理迭代器,那就更好了。

在任何情况下,我们都可以通过使力量从binary_str.len() - 2降到0而不是相反的方式来颠倒顺序。因此,我们可以将代码更改为

while i <= binary_str.len() - 2 {
    result += dbg!(binary_vec[i].to_string().parse::<u32>().unwrap()) * dbg!(2_u32.pow((binary_str.len() - 2 - i) as u32));
    i += 1;
}

我们得到

Enter a decimal: 
100
[src/main.rs:22] binary_vec[i].to_string().parse::<u32>().unwrap() = 1
[src/main.rs:22] 2u32.pow((binary_str.len() - 2 - i) as u32) = 4
[src/main.rs:22] binary_vec[i].to_string().parse::<u32>().unwrap() = 0
[src/main.rs:22] 2u32.pow((binary_str.len() - 2 - i) as u32) = 2
[src/main.rs:22] binary_vec[i].to_string().parse::<u32>().unwrap() = 0
[src/main.rs:22] 2u32.pow((binary_str.len() - 2 - i) as u32) = 1
4

因此,我们看到功效从4正确地降低到了1。我们的答案终于正确了!在更多输入上对其进行测试,并尝试找到一些边缘情况。还有更多的错误可以找到。对代码满意后,取出调试语句。

答案 1 :(得分:0)

我通常会尝试修复代码,但这似乎有点令人费解。

这是一个更简单的解决方案:

use std::io;

fn main() {
    let mut binary = String::new();
    println!("Enter a decimal: ");

    io::stdin().read_line(&mut binary)
        .expect("Couldn't read line");

    match to_decimal(binary) {
        Some(num) => println!("{}", num),
        None => println!("Not a binary!")
    }
}

fn to_decimal(binary_str:String) -> Option<i64> {
    let binary_str = binary_str.trim();

    let mut out_num = 0;
    for c in binary_str.chars() {
        match c {
            '0' => out_num = out_num * 2,
            '1' => out_num = out_num * 2 + 1,
            _ => return None
        }
    }

    Some(out_num)
}