Есть ли черта только для примитивных типов, которые я могу использовать в универсальной функции? - PullRequest
0 голосов
/ 10 мая 2018

Я пытаюсь написать универсальную функцию, которая будет пытаться преобразовать строку в числовой тип, такой как i32, f64 и т. Д. Если строка не преобразуется, то она вернет 0. Я ищу подходящую черту, которую необходимо использовать в моей общей функции ниже:

use std::str::FromStr;

fn get_num_from_str<T: FromStr>(maybe_num_str: &String) -> T {
    let maybe_num = T::from_str(maybe_num_str.as_str());
    if maybe_num.is_ok() {
        return maybe_num.unwrap();
    }
    0 as T
}

fn main() {
    let num_str = String::from("12");
    println!("Converted to i32: {}", get_num_from_str::<i32>(&num_str));
}

Playground Link

Я обнаружил, что у Rust есть черта Primitive, до которой она была удалена. Есть ли что-то еще, что можно использовать вместо этого?

Я нашел обходной путь:

use std::str::FromStr;

fn get_num_from_str<T: Default + FromStr>(maybe_num_str: &String) -> T {
    let maybe_num = T::from_str(maybe_num_str.as_str());
    maybe_num.unwrap_or(Default::default())
}

Playground Link

Это, как предполагает ограничение черты, должно работать для всего, что имеет реализации как для Default, так и FromStr, и я должен переименовать функцию, чтобы отразить это, но было бы неплохо знать, есть ли какая-то особенность только для примитивных числовых типов, которые я могу использовать, чтобы убедиться, что эта функция не может использоваться ни для чего, кроме числовых типов.

Ответы [ 2 ]

0 голосов
/ 10 мая 2018

Нет, такой черты нет. Зачем? Потому что Rust не нужно заботиться о различении «примитивов» от «не примитивов» так же, как и о большинстве других языков. Реально, зачем это ?

Кроме того, обратите внимание, что массивы и необработанные указатели также являются примитивами; Вы действительно хотите включить их?

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

Зачем вам когда-либо хотеть быть искусственно ограниченным на какие типы вы можете использовать? Почему ваша функция не должна работать с любым типом, который соответствует базовым критериям, в которых она нуждается? От кого ты пытаешься защитить себя?

Ваш пример можно сократить, хотя:

fn get_num_from_str<T: Default + FromStr>(s: &str) -> T {
    s.parse().unwrap_or_default()
}
0 голосов
/ 10 мая 2018

Черта Num, определенная в num crate , должна отвечать вашим потребностям, но то, что вы пытаетесь сделать, не является идиоматическим в Rust, поэтому я хотел бы предложить два предложения:

  1. В некоторых языках, таких как C, например, традиционно перегружают определенные значения, такие как 0 или -1, специальными значениями, но было показано, что это является источником путаницы и даже серьезных ошибок.

Вместо этого рассмотрите возможность использования типа Result<T, E>, если ваша функция может не вернуть результат по нескольким причинам, или типа Option<T>, если существует ровно одна причина, по которой ваша функция не может вернуть результат.

В вашем случае вы можете использовать Result<T, E>, чтобы ваша функция могла сообщить, почему преобразование не удалось - содержалась ли строка недопустимых числовых символов? Было ли значение вне диапазона для запрашиваемого типа? Вызывающие абоненты вашей функции, возможно, должны знать, чтобы иметь возможность лучше справляться с ситуацией.

  1. Стандартная библиотека Rust уже включает в себя требуемую функциональность (преобразование строк в значение через универсальную реализацию) в виде std::string::String::parse() или str::parse() методы. По причинам, указанным выше, эти методы действительно возвращают Result.

Используя две вышеуказанные части информации, ваш код теперь можно переписать более надежно, как показано ниже (используя Rust 1.26+ для упрощения обработки ошибок):

type Result<T> = std::result::Result<T, Box<std::error::Error>>;

fn main() -> Result<()> {
    let num_str = String::from("12");
    println!("Converted to i32: {}", num_str.parse::<i32>()?);
    Ok(())
}

пример детской площадки

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

type Result<T> = std::result::Result<T, Box<std::error::Error>>;

fn main() -> Result<()> {
    let num_str = "-1";
    println!("Invalid u32 conversion: {}", num_str.parse::<u32>()?);
    Ok(())
}

пример детской площадки

Это выводит Error: ParseIntError { kind: InvalidDigit } на консоль и возвращает значение std::libc::EXIT_FAILURE вызывающему процессу (означающему, что программа завершилась с ошибкой), все без перегрузки значений или "магических чисел".

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...