Как извлечь два числа из строки и сгенерировать все числа между ними в ржавчине

У меня есть форма пользовательского ввода, которая позволяет им указывать либо одно значение n, либо диапазон в формате start-end. Я хочу сгенерировать вектор всех чисел в диапазоне (только с одной записью в случае n).

У меня есть следующее, которое решает проблему:

fn parse_ports(ports: &str) -> Result<Vec<u32>, ParseError> {
    let ports: Vec<&str> = ports.split("-").collect();

    if ports.len() == 1 {
        Ok(
            ports.iter()
                .map(|x| x.parse().unwrap())
                .collect()
        )
    } else if ports.len() == 2 {
        let start: u32 = ports[0].parse().unwrap();
        let end: u32 = ports[1].parse().unwrap();

        Ok((start..end + 1).collect::<Vec<u32>>())
    } else {
        // At some point this should probably error but that is a tomorrow problem
        Ok(vec![])
    }
}

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

Я чувствую, что смогу сделать это без if. Я также ожидаю, что смогу выполнить некоторую проверку строки вместо того, чтобы иметь дело с случаем len != 1 and len != 2 в if.

Поскольку у вас уже есть рабочий код и вы ищете способы его улучшения, этот вопрос может больше подойти для codereview.stackexchange.com (однако обязательно прочтите Code Review для пользователей SO, прежде чем публиковать там информацию).

Jmb 05.04.2024 10:25

Почему вы хотите собирать диапазоны портов в Vecs? В зависимости от вашего варианта использования может быть предпочтительнее просто вернуть объект RangeInclusive.

Richard Neumann 05.04.2024 10:27
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
2
66
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если у вас есть только одиночные и парные случаи, вы можете использовать Split_once().

Если вы всегда пытаетесь выполнить анализ в случае None, то пустая строка (ports) также неявно вернет ошибку:

use std::num::ParseIntError;

fn parse_ports(ports: &str) -> Result<Vec<u32>, ParseIntError> {
    let (start, end) = if let Some((start, end)) = ports.split_once('-') {
        let start = start.parse()?;
        let end = end.parse()?;
        (start, end)
    } else {
        let start = ports.parse()?;
        (start, start)
    };

    Ok((start..end + 1).collect())
}

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

fn parse_ports(ports: &str) -> Result<Vec<u32>, ParseIntError> {
    let (start, end) = match ports.split_once('-') {
        Some((start, end)) => (start, Some(end)),
        None => (ports, None),
    };

    let start = start.parse()?;
    let end = end.map(|end| end.parse()).transpose()?;

    Ok(match end {
        Some(end) => (start..=end).collect(),
        None => vec![start],
    })
}

Я почти уверен, что это имеет такое же/желаемое поведение и намного проще, чем оба примера здесь: play.rust-lang.org/…

cdhowie 05.04.2024 02:06

@cdhowie Да, я согласен, но ОП сказал что-то о промежуточной проверке. Поэтому мне хотелось четко отделить парсинг от сбора

vallentin 05.04.2024 02:34

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

use std::num::ParseIntError;
use std::ops::RangeInclusive;

const PORTS_DELIMITER: char = '-';

/// Parse a port range.
///
/// # Errors
/// Returns a [`ParseIntError`] if the port numbers could not be parsed.
///
/// # Examples
/// ```
/// use parse_ports::parse_port_range;
///
/// assert_eq!(parse_port_range("123"), Ok(123..=123));
/// assert_eq!(parse_port_range("42-1337"), Ok(42..=1337));
///
/// for (index, port) in parse_port_range("0-65535").unwrap().enumerate() {
///     assert_eq!(index as u32, port);
/// }
///
/// assert!(parse_port_range("1-10").unwrap().contains(&7));
/// ```
pub fn parse_port_range(port_range: &str) -> Result<RangeInclusive<u32>, ParseIntError> {
    let (start, end) = port_range
        .split_once(PORTS_DELIMITER)
        .unwrap_or((port_range, port_range));
    Ok(start.parse()?..=end.parse()?)
}

Другие вопросы по теме