Разница между std :: convert :: TryFrom <T>и оператором as - PullRequest
0 голосов
/ 17 апреля 2020

Я пытаюсь записать обобщенную c строку в числовую функцию в Rust, где поддерживаются следующие типы: i16, i32, i64, u32, u64, f32 и f64. Первоначально у меня было это:

fn str_to_num<N>(s: &str, default_res: N) -> N
where
    N: FromStr,
{
    if let Ok(n) = N::from_str(s) {
        return n;
    }
    default_res
}

И это работало нормально, пока меня не попросили поддержать также шестнадцатеричные строки. Поскольку мы хотим анализировать только шестнадцатеричные строки как целые числа, у меня теперь есть две версии функции:

use std::convert::TryFrom;
use std::str::FromStr;

fn str_to_num_with_hex<N>(s: &str, default_res: N) -> N
where
    N: FromStr + TryFrom<u64>,
{
    if s.starts_with("0x") || s.starts_with("0X") {
        if let Ok(n) = u64::from_str_radix(&s[2..], 16) {
            if let Ok(n) = N::try_from(n) {
                return n;
            }
        }
    }
    return str_to_num(s, default_res);
}

fn str_to_num<N>(s: &str, default_res: N) -> N
where
    N: FromStr,
{
    if let Ok(n) = N::from_str(s) {
        return n;
    }
    default_res
}

Однако при тестировании функции с шестнадцатеричной строкой, которая вписывается в u64, но не в i64 Кажется, что TryFrom и as ведут себя по-разному. Есть ли способ достичь семантики as в сочетании с генериками?

fn main() {
    let hex = "0xB85991EE5DA2B557";
    let unsigned_long: u64 = str_to_num_with_hex(hex, 0);
    let signed_long: i64 = str_to_num_with_hex(hex, 0);
    println!("{}", unsigned_long); // prints 13283809028865176919
    println!("{}", signed_long); // prints 0
    println!("{}", unsigned_long as i64); // prints -5162935044844374697
}

1 Ответ

1 голос
/ 17 апреля 2020

Я так не думаю. as буквально не может выйти из строя, и, к сожалению, для удобства и в соответствии с существующими языками Rust решил реализовать as для потенциально потерянных чисел c преобразований, например isize as u8.

try_from может на самом деле провал, вот и весь смысл. Поэтому, если он столкнется с конверсией с потерями (вход за пределы диапазона), он потерпит неудачу, вот в чем суть.

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

trait FromHex where Self: Sized {
    fn from_hex(s: &str) -> Option<Self>;
}

impl FromHex for u64 {
    fn from_hex(s: &str) -> Option<u64> {
        u64::from_str_radix(s, 16).ok()
    }
}
impl FromHex for i64 {
    fn from_hex(s: &str) -> Option<i64> {
        u64::from_hex(s).map(|v| v as i64)
    }
}

fn main() {
    let s = "B85991EE5DA2B557";
    println!("{:?} {:?}", u64::from_hex(s), i64::from_hex(s));
}

conv может также делать из коробки то, что вы хотите.

...