Rust是否具有等同于F#typedef的习惯用法? [英] Does Rust have an idiomatic equivalent to F# typedefs?

查看:250
本文介绍了Rust是否具有等同于F#typedef的习惯用法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm re-writing existing code of mine in Rust 1.6 and I've found it very convenient in the source language to label a type by typedef. For example, in my card game I have a rank value in F# defined as:

type Rank = uint8

推荐答案

来自 The Rust Programming标题为 Rust提供了声明类型别名的功能,以为现有类型提供另一个名称.为此,我们使用type关键字.例如,我们可以像这样将别名Kilometers创建为i32:

Rust provides the ability to declare a type alias to give an existing type another name. For this we use the type keyword. For example, we can create the alias Kilometers to i32 like so:

type Kilometers = i32;

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

Now, the alias Kilometers is a synonym for i32; [...], Kilometers is not a separate, new type. Values that have the type Kilometers will be treated the same as values of type i32:

type Kilometers = i32;

let x: i32 = 5;
let y: Kilometers = 5;

println!("x + y = {}", x + y);

您还应该阅读更多内容,但这可以回答问题.

There's more that you should read, but this answers the question.

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

As a bit of editorial, I don't think that a type alias is a great fit in a lot of places that people use them. Assuming that your Rank type represents something to do with a deck of cards, I'd suggest either an enum or a newtype. The reason is that with a type alias you can do something like this:

let rank: Rank = 100;

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

Which is nonsensical for a typical deck of cards. An enum is a restricted set. This means you can never create an invalid 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 只是包装器类型.与包装的类型相比,它不占用额外的空间,它只是提供了一种实际的新类型,使您可以实现可以限制为有效值的方法.可以创建无效值,但只能在您自己的代码中,而不能在所有客户端代码中创建:

A newtype is just a wrapper type. It consumes no extra space compared to the wrapped type, it just provides an actual new type that lets you implement methods that can restrict to valid values. It's possible to create invalid values, but only within your own code, not all client code:

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
    }
}

我倾向于将类型别名用作类型的快速占位符.在编写上述示例时,我实际上写了:

I tend to use type aliases as quick placeholders of types. While writing the above examples, I actually wrote:

type Error = ();

并返回了Result<Rank, Error>,但随后认为这会造成混淆. :-)

And returned a Result<Rank, Error>, but then thought that would be confusing. :-)

我使用它们的另一种情况是缩短我不想隐藏的更大的类型.发生这种情况的原因是迭代器或Result之类的类型,您可以参见标准库.像这样:

The other case I use them is to shorten a larger type that I don't want to hide. This happens with types like iterators or Results, which you can see in the standard library. Something like:

type CardResult<T> = Result<T, Error>;

fn foo() -> CardResult<String> {
    // ..
}

这篇关于Rust是否具有等同于F#typedef的习惯用法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆