Rust Book Глава 8 — Текстовый интерфейс для управления сотрудниками

Используя хэш-карту и векторы, создайте текстовый интерфейс, позволяющий пользователю добавлять имена сотрудников в отдел компании. Например, «Добавить Салли в Инженерное дело» или «Добавьте Амира в продажи.Затем позвольте пользователю получить список всех людей в отделе или всех людей в компании по отделам, отсортированный по алфавиту.

Недавно мне очень понравилось изучать Rust и читать книгу о Rust, и я хочу получить небольшой отзыв о своем ответе на этот вызов из главы 8.

Я намеренно сделал все довольно кратко и просто — я ответил на вопрос очень буквально, и все критерии соблюдены, насколько я могу судить по результатам тестирования.

Я жестко закодировал отделы в хэш-карту и не очень старался обрабатывать крайние случаи или неправильный ввод. Пользователям также приходится вводить очень неуклюжие команды с точной формулировкой.

Интерфейс не очень удобный, так как после каждой команды цикл перезапускается и каждый раз отображается приветственное сообщение.

Не стесняйтесь оставлять отзывы по любому из вышеперечисленных вопросов, но знайте, что я, по крайней мере, осведомлен об этих проблемах.

Больше всего меня беспокоит идиоматический Rust и структура кода.

Есть ли лучший способ справиться с различными условиями, чем с if / else if / else?

Я не хотел вкладывать слишком много циклов в основную функцию для удобочитаемости и ради собственного здравомыслия. Если бы я хотел сделать UX немного более плавным, имело бы смысл создать функцию для каждой команды, которую можно было бы вызывать повторно, например, «Добавить сотрудников», «Просмотреть отдел» с внутренним циклом?

Что-нибудь еще, что выделяется, что я делаю совершенно неправильно?

Что-нибудь, что выделяется, что я делаю правильно?

use std::collections::HashMap;
use std::io;

fn main() {
    let mut company: HashMap<String, Vec<String>> = HashMap::new();

    company.insert("engineering".to_string(), Vec::new());
    company.insert("operations".to_string(), Vec::new());
    company.insert("sales".to_string(), Vec::new());

    loop {
        println!("Welcome! What would you like to do?\n 
                  To add employee to a department, type 'Add <employee name> to <department name> (engineering, operations, or sales),\n 
                  To view employees in a department, type 'View department <department name>',\n
                  To view all employees by department, type 'View all',
                  To exit, type 'exit'.
        ");

        let mut entry = String::new();
        io::stdin()
            .read_line(&mut entry)
            .expect("Failed to read line");

        let entry: &str = entry.trim();
        let mut split = entry.split_whitespace();

        if entry.contains("Add") {
            let emp = split.nth(1).unwrap().to_string();
            let dep = split.nth(1).unwrap().to_lowercase();
            println!("{emp} added to {dep}");

            let vec = company.get_mut(&dep).unwrap();
            vec.push(emp);
            vec.sort_by_key(|name| name.to_lowercase());
            println!("{:?}", company)
        } else if entry.contains("View department") {
            let dep = split.nth(2).unwrap().to_lowercase();
            let view = &company[&dep];

            println!("Employees in {dep}:\n");
            for v in view {
                println!("{v}\n");
            }
        } else if entry.contains("View all") {
            for k in company.keys() {
                let view = &company[k];
                println!("Department: {k}");
                for v in view {
                    println!("{v}\n");
                }
            }
        } else if entry == "exit" {
            break;
        } else {
            println!("Please enter a valid option");
        }
    }
}

1 ответ
1

Это уже очень хорошо, но есть несколько вещей, которые я бы изменил. Во-первых, я представил различные методы, чтобы сделать main немного короче.

fn view_department(company: &HashMap<String, Vec<String>>, department: &str) {
    println!("Employees in {department}:\n");
    for e in &company[department] {
        println!("{e}\n");
    }
}

fn view_all(company: &HashMap<String, Vec<String>>) {
    for (department, employees) in company {
        println!("Department: {department}");
        for e in employees {
            println!("{e}\n");
        }
    }
}

fn add_employee(company: &mut HashMap<String, Vec<String>>, employee: &str, department: &str) {
    println!("{employee} added to {department}");

    let vec = company.get_mut(department).unwrap();
    vec.push(employee.to_owned());
    vec.sort_by_key(|name| name.to_lowercase());
    println!("{company:?}")
}

Обратите внимание, что я повторяю непосредственно пары ключ-значение хэш-карты, что немного эффективнее, чем двойное хеширование.

Вы можете просто использовать многострочную строку, чтобы избавиться от экранирования новой строки:

        println!(
"Welcome! What would you like to do?
To add employee to a department, type 'Add <employee name> to <department name> (engineering, operations, or sales),
To view employees in a department, type 'View department <department name>',
To view all employees by department, type 'View all',
To exit, type 'exit'."
        );

И самое главное, поскольку проблема заключается в сопоставлении разных строковых шаблонов, я использовал для этого сопоставление шаблонов:

match entry.split_whitespace().collect::<Vec<&str>>().as_slice() {
    ["Add", emp, "to", dep] => add_employee(&mut company, emp, dep),
    ["View", "department", dep] => view_department(&company, dep),
    ["View", "all"] => view_all(&company),
    ["exit"] => break,
    _ => println!("Please enter a valid option")
}

Я думаю, что это более читабельно, чем версия с большим количеством nth и магические индексы.

Примечание: изначально я написал entry.trim().split_whitespace()но резко сообщил мне, что trim не нуженпоэтому чаще используйте clippy и прислушивайтесь к его советам.

Вот полный основной для справки:

fn main() {
    let mut company: HashMap<String, Vec<String>> = HashMap::new();

    company.insert("engineering".to_string(), Vec::new());
    company.insert("operations".to_string(), Vec::new());
    company.insert("sales".to_string(), Vec::new());

    loop {
        println!(
"Welcome! What would you like to do?
To add employee to a department, type 'Add <employee name> to <department name> (engineering, operations, or sales),
To view employees in a department, type 'View department <department name>',
To view all employees by department, type 'View all',
To exit, type 'exit'."
        );

        let mut entry = String::new();
        io::stdin()
            .read_line(&mut entry)
            .expect("Failed to read line");

        match entry.split_whitespace().collect::<Vec<&str>>().as_slice() {
            ["Add", emp, "to", dep] => add_employee(&mut company, emp, dep),
            ["View", "department", dep] => view_department(&company, dep),
            ["View", "all"] => view_all(&company),
            ["exit"] => break,
            _ => println!("Please enter a valid option")
        }
    }
}
```

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *