【问题标题】:Does Rust have an idiomatic equivalent to F# typedefs?Rust 是否具有与 F# typedefs 的惯用等效项?
【发布时间】:2016-04-28 13:33:16
【问题描述】:

我正在用 Rust 1.6 重写我的现有代码,我发现在源语言中通过 typedef 标记类型非常方便。例如,在我的纸牌游戏中,我在 F# 中定义为:

type Rank = uint8

【问题讨论】:

    标签: types rust type-alias


    【解决方案1】:

    来自The Rust Programming Language 标题为Creating Type Synonyms with Type Aliases 的部分:

    Rust 提供了声明 类型别名 的能力,从而为现有类型提供另一个名称。为此,我们使用type 关键字。例如,我们可以像这样创建别名Kilometersi32

    type Kilometers = i32;
    

    现在,别名Kilometersi32 的同义词; [...],Kilometers 不是一个单独的新类型。 Kilometers 类型的值将被视为与 i32 类型的值相同:

    type Kilometers = i32;
    
    let x: i32 = 5;
    let y: Kilometers = 5;
    
    println!("x + y = {}", x + y);
    

    您应该阅读更多内容,但这回答了问题。


    作为一点编辑,我认为类型别名在人们使用它们的许多地方并不适合。假设您的Rank 类型代表与一副纸牌有关的内容,我建议使用enumnewtype。原因是使用类型别名可以执行以下操作:

    let rank: Rank = 100;
    

    这对于一副典型的纸牌来说是荒谬的。枚举是一个受限集。这意味着您永远不能创建无效的Rank

    enum Rank {
        One, Two, Three, Four, Five,
        Six, Seven, Eight, Nine, Ten,
        Jack, Queen, King, Ace,
    }
    
    impl Rank {
        fn from_value(v: u8) -> Result<Rank, ()> {
            use Rank::*;
    
            let r = match v {
                1 => One,
                2 => Two,
                // ...
                _ => return Err(()),
            };
            Ok(r)
        }
    
        fn value(&self) -> u8 {
            use Rank::*;
    
            match *self {
                One => 1,
                Two => 2,
                // ...
            }
        }
    }
    

    newtype 只是一个包装类型。与包装类型相比,它不消耗额外的空间,它只是提供了一个实际的新类型,允许您实现可以限制为有效值的方法。可以创建无效值,但只能在您自己的代码中,而不是所有客户端代码中:

    struct Rank(u8);
    
    impl Rank {
        fn from_value(v: u8) -> Result<Rank, ()> {
            if v >= 1 && v <= 14 {
                Ok(Rank(v))
            } else {
                Err(())
            }
        }
    
        fn value(&self) -> u8 {
            self.0
        }
    }
    

    我倾向于使用类型别名作为类型的快速占位符。在写上面的例子时,我实际上是这样写的:

    type Error = ();
    

    并返回了Result&lt;Rank, Error&gt;,但后来认为这会令人困惑。 :-)

    我使用它们的另一种情况是缩短我不想隐藏的较大类型。像迭代器或Results 这样的类型会发生这种情况,您可以使用see in the standard library。比如:

    type CardResult<T> = Result<T, Error>;
    
    fn foo() -> CardResult<String> {
        // ..
    }
    

    【讨论】:

    • 感谢您快速详细的回复!通常我非常同意你使用枚举将我的值限制为标准甲板值。但是,就我而言,我一般使用“卡片”一词,而不是纸牌,所以我需要一个未指定值的等级。我是否正确理解,如果我想要简单的替换,我应该使用类型别名,如果我想施加额外的限制/逻辑,我应该使用新类型?
    • @ErikUggeldahl 是的,这听起来是正确的。新类型允许额外的限制或逻辑,而别名​​就是这样,同一事物的另一个名称。
    • @ErikUggeldahl 这不仅仅是限制值。别名不提供额外的类型安全,因为它不是……新类型,而是同一类型的另一个名称。能够计算Rank / 5 或将Rank 传递给采用u8 的函数是否有意义?因为类型别名允许您这样做(因为Rank u8)。当你有一个非常长和复杂的类型并且想要更简洁地引用它时,你通常在 Rust 中使用类型别名是为了方便(但别名 类型,应该用来代替它无处不在)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-11
    • 2013-08-11
    • 2016-05-11
    • 2011-10-30
    • 2018-09-15
    相关资源
    最近更新 更多