Search code examples
rustclap

Panic while parsing an integer with Clap in Rust


I'm attempting to parse a usize from the command line with clap. It compiles with no warnings, and cargo clippy -- -W clippy::pedantic returns no issues. However, it panics, even with the default value.

My Cargo.toml dependencies looks like this

[dependencies]
clap = "3.2.8"
itertools = "0.10.3"

And the actual code is this

use std::collections::HashMap;

use itertools::{self, Itertools};

use clap::{arg, Command};

fn ngram_freq<'a>(s: &'a [&str], n: usize) -> HashMap<&'a [&'a str], usize> {
    s.windows(n).counts()
}

fn main() {
    let args = Command::new("ngram_freq")
        .version("0.1.0")
        .author("Nathan McIntosh")
        .about("Reads in a file or a set of files, and returns most popular n-grams")
        .arg(
            arg!(-f --filenames <FILENAME>)
            .required(false)
            .help("One or more files to read").multiple_values(true))
        .arg(
            arg!(-t --threshold <THRESHOLD>)
            .required(false)
            .help("Only show ngrams with that occur with at a frequency above this threshold")
            .default_value("2"))
        .get_matches();

    dbg!(&args);

    let threshold = &args
        .try_get_one::<usize>("threshold")
        .expect("Could not read a threshold value")
        .expect("Could not parse threshold value");

    let filenames: Vec<String> = args
        .get_many("filenames")
        .expect("Could not get filenames")
        .cloned()
        .collect();

    let file_contents: Vec<String> = filenames
        .iter()
        .map(std::fs::read_to_string)
        .filter(std::result::Result::is_ok)
        .map(std::result::Result::unwrap)
        .collect();

    let words: Vec<_> = file_contents
        .iter()
        .flat_map(|f| f.split_whitespace())
        .collect();

    for n in 1..=3 {
        ngram_freq(&words, n)
            .iter()
            .filter(|(_, &v)| v > **threshold)
            .sorted_by(|a, b| b.1.cmp(a.1))
            .for_each(|x| println!("{:?}", x));
    }
}

And here is how I call it, and the error returned

RUST_BACKTRACE=full ./target/release/ngram_freq -f ~/dev/aoc_2020/*.jl                                                                                                                                                                ─╯
[src/main.rs:27] &args = ArgMatches {
    args: {
        [hash: 2DA78AE236ADEBDA]: MatchedArg {
            occurs: 1,
            source: Some(
                CommandLine,
            ),
            indices: [
                2,
                3,
                4,
                5,
                6,
                7,
                8,
                9,
                10,
                11,
                12,
                13,
                14,
                15,
                16,
                17,
                18,
                19,
                20,
                21,
                22,
                23,
                24,
                25,
            ],
            type_id: Some(
                TypeId {
                    t: 4857865405506106372,
                },
            ),
            vals: [
                [
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                ],
            ],
            raw_vals: [
                [
                    "/Users/natemcintosh/dev/aoc_2020/day01.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day02.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day03.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day04.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day05.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day06.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day07.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day08.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day09.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day10.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day11.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day12.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day13.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day14.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day15.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day16.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day17.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day18.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day19.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day21.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day22.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day23.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day24.jl",
                    "/Users/natemcintosh/dev/aoc_2020/day25.jl",
                ],
            ],
            ignore_case: false,
        },
        [hash: 394AC800F94DBDC5]: MatchedArg {
            occurs: 0,
            source: Some(
                DefaultValue,
            ),
            indices: [
                26,
            ],
            type_id: Some(
                TypeId {
                    t: 4857865405506106372,
                },
            ),
            vals: [
                [
                    AnyValue {
                        inner: TypeId {
                            t: 4857865405506106372,
                        },
                    },
                ],
            ],
            raw_vals: [
                [
                    "2",
                ],
            ],
            ignore_case: false,
        },
    },
    subcommand: None,
}
thread 'main' panicked at 'Could not read a threshold value: Downcast { actual: TypeId { t: 4857865405506106372 }, expected: TypeId { t: 13834754221672687376 } }', src/main.rs:31:10
stack backtrace:
   0:        0x1044a5bd8 - std::backtrace_rs::backtrace::libunwind::trace::ha03082fdb36b8e44
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5
   1:        0x1044a5bd8 - std::backtrace_rs::backtrace::trace_unsynchronized::hf1acb3d3fce46837
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
   2:        0x1044a5bd8 - std::sys_common::backtrace::_print_fmt::hbc7deaf5374edfd8
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/sys_common/backtrace.rs:66:5
   3:        0x1044a5bd8 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::ha9a1a8bf7485458d
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/sys_common/backtrace.rs:45:22
   4:        0x1044baf68 - core::fmt::write::h9ee5b099821ae5e1
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/core/src/fmt/mod.rs:1196:17
   5:        0x1044a376c - std::io::Write::write_fmt::h240449cbf2a536ee
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/io/mod.rs:1654:15
   6:        0x1044a7178 - std::sys_common::backtrace::_print::hf65124e94f800e0d
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/sys_common/backtrace.rs:48:5
   7:        0x1044a7178 - std::sys_common::backtrace::print::haec7f207d91c0faa
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/sys_common/backtrace.rs:35:9
   8:        0x1044a7178 - std::panicking::default_hook::{{closure}}::h321649ccf26de565
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:295:22
   9:        0x1044a6e54 - std::panicking::default_hook::h1e736e6294672e8d
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:314:9
  10:        0x1044a7650 - std::panicking::rust_panic_with_hook::hd083a3aa5c934ce6
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:698:17
  11:        0x1044a7544 - std::panicking::begin_panic_handler::{{closure}}::hb5be8aaa10a229ca
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:588:13
  12:        0x1044a60b4 - std::sys_common::backtrace::__rust_end_short_backtrace::h68e10e4f00198298
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/sys_common/backtrace.rs:138:18
  13:        0x1044a729c - rust_begin_unwind
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:584:5
  14:        0x1044c50fc - core::panicking::panic_fmt::hffc63a015c61fdde
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/core/src/panicking.rs:142:14
  15:        0x1044c5158 - core::result::unwrap_failed::hd24fd75dfe8b9563
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/core/src/result.rs:1785:5
  16:        0x104444788 - ngram_freq::main::hd9db2bf6cddd0327
  17:        0x104441058 - std::sys_common::backtrace::__rust_begin_short_backtrace::h058b83e5b54a46ca
  18:        0x1044461bc - std::rt::lang_start::{{closure}}::hc0275131429ecb59
  19:        0x10449f424 - core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once::h3b331d3fbbcc71c7
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/core/src/ops/function.rs:280:13
  20:        0x10449f424 - std::panicking::try::do_call::hff616b6be7bbc015
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:492:40
  21:        0x10449f424 - std::panicking::try::h298ba2ba2d72fd07
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:456:19
  22:        0x10449f424 - std::panic::catch_unwind::h82d7a0ad94159423
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panic.rs:137:14
  23:        0x10449f424 - std::rt::lang_start_internal::{{closure}}::h801a38d4a43d99be
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/rt.rs:128:48
  24:        0x10449f424 - std::panicking::try::do_call::h8ecac938bf293f4a
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:492:40
  25:        0x10449f424 - std::panicking::try::he8bacc15da046b2c
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:456:19
  26:        0x10449f424 - std::panic::catch_unwind::hfbee3bd78d2873c3
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panic.rs:137:14
  27:        0x10449f424 - std::rt::lang_start_internal::hbbfe6e663b24763e
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/rt.rs:128:20
  28:        0x104444a64 - _main

Solution

  • Figured it out myself.

    The issue was that I needed to add a value_parser to the clap builder.

    The only thing that changed was the clap Command builder

    let args = Command::new("ngram_freq")
            .version("0.1.0")
            .author("Nathan McIntosh")
            .about("Reads in a file or a set of files, and returns most popular n-grams")
            .arg(
                arg!(-f --filenames <FILENAME>)
                    .required(false)
                    .help("One or more files to read")
                    .multiple_values(true),
            )
            .arg(
                arg!(-t --threshold <THRESHOLD>)
                    .required(false)
                    .help("Only show ngrams with that occur with at a frequency above this threshold")
                    .default_value("2")
                    .value_parser(clap::value_parser!(usize)),
            )
            .get_matches();
    

    That said, I do wish that the compiler or clippy could have told me this