【问题标题】:How do I use the blanket Into trait implementation to convert back to the original type?如何使用毯子 Into 特征实现转换回原始类型?
【发布时间】:2020-07-08 13:56:27
【问题描述】:

From trait 的文档有以下关于 trait 本身和任何可能的失败的说明:

人们应该总是更喜欢实现From 而不是Into,因为实现From 会自动提供Into 的实现,这要归功于标准库中的一揽子实现。

注意:这个特性不能失败。如果转换失败,请使用TryFrom

我有以下From 实现将整数转换为罗马数字:

/// ### UpperRoman
/// Upper-case Roman numerals. The `None` variant is for handling invalid conversions.
pub enum UpperRoman {
    M,
    CM,
    D,
    CD,
    C,
    XC,
    L,
    XL,
    X,
    IX,
    V,
    IV,
    I,
    None,
}

impl From<u32> for UpperRoman {
    /// ### from
    /// Converts a `u32` to an `UpperRoman` numeral variant.
    fn from(numeral: u32) -> Self {
        match numeral {
            1000 => Self::M,
            900 => Self::CM,
            500 => Self::D,
            400 => Self::CD,
            100 => Self::C,
            90 => Self::XC,
            50 => Self::L,
            40 => Self::XL,
            10 => Self::X,
            9 => Self::IX,
            5 => Self::V,
            4 => Self::IV,
            1 => Self::I,
            _ => Self::None,
        }
    }
}

只有少数有效案例。对于任何无效输入,我定义了一个变体UpperRoman::None,这意味着没有与给定输入匹配的罗马数字。这会处理任何失败情况,因此满足文档中设置的要求。

如果我想使用自动生成的Into-trait 实现将罗马数字转换回整数怎么办? UpperRoman::into 函数如何处理 UpperRoman::None 情况?在调用函数之前,我唯一的选择是匹配UpperRoman::into 的可能输入以确保它不是UpperRoman::None 变体吗?

【问题讨论】:

  • Into&lt;u32&gt; for UpperRoman 没有自动生成的实现。将有一个Into&lt;UpperRoman&gt; for u32 的实现,即对于您手动实现的相同转换。
  • "自动生成Into-trait 实现将罗马数字转换回整数" → 没有这样的东西。当您定义From&lt;u32&gt; for UpperRoman 时,您会自动获得Into&lt;UpperRoman&gt; for u32,而不是Into&lt;u32&gt; for UpperRomanFrom 不需要像您发现的那样是双射的,也不需要按照您的方式实现,因此编译器无法猜测如何生成其他方式。
  • UpperRoman 中不包含None 的情况会更惯用,而是在值可能为None 时使用TryFromOption&lt;UpperRoman&gt;

标签: enums rust type-conversion traits


【解决方案1】:

这里有一个混淆。鉴于您对From&lt;u32&gt; for UpperRoman 的实现,编译器不会自动为您提供相反的转换(即从UpperRomanu32)。相反,它将为您提供双重实现Into&lt;UpperRoman&gt; for u32,它在语义上具有相同的方向。 Into 的这种全面实现将允许程序员编写以下任一代码:

let c = UpperRoman::from(100);
let c: UpperRoman = 100.into();

话虽如此,这意味着您需要努力进行有意义的相反转换。在这种情况下,最好假设从u32UpperRoman 的转换是一个容易出错的转换,因为并非所有整数都映射到单个罗马数字。 这也将允许您删除 None 变体,它仅用作非常不需要的“空”值,并且无论如何都可以通过 Option 添加到任何类型。

use std::convert::TryFrom;

pub enum UpperRoman {
    M,
    CM,
    D,
    CD,
    C,
    XC,
    L,
    XL,
    X,
    IX,
    V,
    IV,
    I,
}

impl TryFrom<u32> for UpperRoman {
    type Error = &'static str;

    fn try_from(numeral: u32) -> Result<Self, Self::Error> {
        match numeral {
            1000 => Ok(Self::M),
            900 => Ok(Self::CM),
            // expand the rest here
            4 => Ok(Self::IV),
            1 => Ok(Self::I),
            _ => Err("that is no good!"),
        }
    }
}

(为这种转换创建更好的错误类型留给读者练习。)

完成后,将UpperRoman 转换为u32 很简单:

impl From<UpperRoman> for u32 {
    fn from(v: UpperRoman) -> Self {
        Self::M => 1000,
        // ... expand the remaining variants exhaustively here, no None
    }
}

另见:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-02
    • 2019-12-08
    • 1970-01-01
    • 2015-01-12
    • 1970-01-01
    • 2010-12-29
    相关资源
    最近更新 更多