首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >连接和映射Rust中的命令行参数

连接和映射Rust中的命令行参数
EN

Code Review用户
提问于 2019-07-27 14:55:35
回答 3查看 494关注 0票数 3

有什么更自然的方法在生锈时这样做吗?我很难把这个弄干净。类型错配和借来让我很困惑。我能做些什么再因素?

输入

  • 输入空格分隔的命令行参数列表。

输出

  • 书籍(加入除最后一个外的所有参数)
  • 参考(最后一个论点)
  • 档案(通过这本书查阅)

我将books_to_files映射缩写为只包含几个不同单词长度的书籍示例。

示例

代码语言:javascript
复制
In:                             Out:

Genesis 1:1                     Genesis, 1:1, mhc1.txt
Song of Solomon 2:2-4           Song of Solomon, 2:2-4, mhc3.txt
Acts of the Apostles 3:3-9      Acts of the Apostles, 3:3-9, mhc6.txt

源代码

代码语言:javascript
复制
use std::env;
use std::collections::HashMap;

fn main() {
    let args: Vec<String> = env::args().collect();

    let book: String;
    if args.len() == 3 {
        book = args[1].clone();
    } else if args.len() == 4 {
        book = args[1].clone() + " " + &args[2].clone();
    } else if args.len() == 5 {
        book = args[1].clone() + " " + &args[2].clone() + " " + &args[3].clone();
    } else if args.len() == 6 {
        book = args[1].clone() + " " + &args[2].clone() + " " + &args[3].clone() + " " + &args[4].clone();
    } else {
        panic!("Wrong number of arguments");
    }
    let reference: String = args[args.len()-1].clone(); 
    let file: String = get_file(&book);

    println!("{}, {}, {}", book, reference, file);
}

fn get_file(book: &String) -> String {
    let books_to_files: HashMap<&str, &str> = [
        ("Genesis", "mhc1.txt"),
        ("First Samuel", "mhc2.txt"),
        ("Song of Solmon", "mhc3.txt"),
        ("Acts of the Apostles", "mhc6.txt"),
        ("First Corinthians", "mhc6.txt"),
        ("Galatians", "mhc6.txt"),
    ].iter().cloned().collect();

    books_to_files.get::<str>(&book.to_string()).unwrap().to_string()
}

```
代码语言:javascript
复制
EN

回答 3

Code Review用户

发布于 2019-07-29 16:19:12

我收集了一些关于可能的重构因素的想法,您可以在下面考虑。

您的if链不是

最有效的方式

正如您所写的,您的代码只适用于长度在1到4个单词之间的标题。这不是一个灾难,它可能会对大多数标题有效,但我们可以重写您的代码,使其更短,并处理任意长度的标题。双赢!

  • 我们可以使用Iterator::skip()来丢弃可执行名称,因为我们根本不关心它。请参阅文档中的skip()。设args: Vec = env::args().skip(1).collect();
  • 在继续之前检查一下我们是否有足够的论据是个好主意。让arg_len: usize = args.len();如果arg_len <2{恐慌!(“至少必须提供两个参数!”);}
  • 现在,我们可以稍微聪明一点,避免将您的if链放在一起。使用范围,我们可以得到与除最后一个元素以外的所有元素对应的args向量的切片。然后,我们可以使用join()将该切片转换为String。例如,如果我们从vec!["Song", "of", "Solomon", "2:2-4"]args中开始,我们将拿出一个片段来获取["Song", "of", "Solomon"],然后将它们与中间的" "重新连接,以获得"Song of Solomon"。设book = args(Arg)_(第一条).join(“");

试图避免clone(),除非有必要

当我查看您的代码时,我注意到的第一件事是,您已经在很多地方使用了clone()。我们已经用上面的调整处理掉了很多这样的问题,当你不需要分配的时候,你应该尽量避免它们,因为这是浪费时间和记忆。如果您可以使用&str,那么可以这样做,而不是坚持所有字符串都是String。顺便说一句,&String很少有问题--只需在那里使用&str即可。

  • 将上述内容付诸实施,我们可以让reference成为一个&str,并避免克隆。让我们参考:&str = &argsargs.len() -1;
  • 让我们也更改get_book的S方法签名,如上面建议的. fn get_file(book:&str) -> &str {
  • 现在我们可以去掉下面不必要的to_string()了。books_to_files.get::(&book.to_string()).unwrap()
  • 并在main()中调整这一行以接受&str。让档案:&str = get_file(&book);

避免展开

理想情况下,get_book应该返回一个Option,这样调用方就可以选择如何处理错误。因为它是致命的无论如何,展开没有什么区别,但值得记住,作为一个未来的改进。

在网上试试

票数 3
EN

Code Review用户

发布于 2019-07-30 01:07:41

我们可以使用一个匹配块,而不是使用hashmap来查找书籍。(这是假设您的书是在编译时就知道的。)此外,我们还可以返回一个Option<&'static str>。该选项允许函数的调用方决定如何处理失败,而&'static str是字符串文本的类型。

代码语言:javascript
复制
fn get_file(book: &str) -> Option<&'static str> {
    match book {
        "Genesis" => Some("mhc1.txt"),
        "First Samuel" => Some("mhc2.txt"),
        "Song of Solmon" => Some("mhc3.txt"),
        "Acts of the Apostles" => Some("mhc6.txt"),
        "First Corinthians" => Some("mhc6.txt"),
        "Galatians" => Some("mhc6.txt"),
        _ => None,
    }
}

根据您的响应,我建议使用第一个命令行参数作为整本书的名称。我会写像这样的main。我们可以使用切片模式来检查参数的正确数量,并同时将它们绑定到变量。然后,我们可以尝试找到文件或打印错误消息,否则。如果参数数是错误的,我们还打印了一个错误,显示程序的正确用法。

代码语言:javascript
复制
fn main() {
    let args: Vec<String> = std::env::args().collect();

    if let [_, book, reference] = args.as_slice() {
        if let Some(file) = get_file(book) {
            println!("{}, {}, {}", book, reference, file);
        } else {
            eprintln!("Could not find book!");
        }
    } else {
        eprintln!("Usage: {} <BOOK> <REFERENCE>", args[0]);
    }
}
票数 2
EN

Code Review用户

发布于 2019-08-12 13:42:53

以下是一些改进:

  • 如果你不想要第一个arg,skip it
    • env::args().skip(1).collect()

  • 使用pop获取引用(最后一个arg)
  • join 获取这本书(剩余的args)
    • args.join(" ")

  • 如果可能的话使用&str
    • get_file(book: &str)

  • 将书与文件匹配,使用match
    • match book {...}

  • 使用Option/Some/None,以便main可以处理错误
    • get_file(...) -> Option<&str>

代码语言:javascript
复制
use std::env;

fn main() {
    let mut args: Vec<String> = env::args().skip(1).collect();
    let reference = args.pop()
        .expect("Expected 2+ parameters: <book of the Bible> <reference>");
    let book = args.join(" ");
    let file = get_file(&book)
        .expect("Not a valid book");
    println!("{}, {}, {}", book, reference, file);
}

fn get_file(book: &str) -> Option<&str> {
    match book {
        "Genesis" => Some("mhc1.txt"),
        "First Samuel" => Some("mhc2.txt"),
        "Song of Solmon" => Some("mhc3.txt"),
        "Acts of the Apostles" => Some("mhc6.txt"),
        "First Corinthians" => Some("mhc6.txt"),
        "Galatians" => Some("mhc6.txt"),
        _ => None
    }
}

感谢Aurora0001JayDepp提供的有益反馈(占上述建议的90% )。这段代码现在更自然了。

票数 0
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/225035

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档