Does Rust have an idiomatic equivalent to F# typedefs?

℡╲_俬逩灬. 提交于 2020-01-01 04:37:49

问题


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

回答1:


From The Rust Programming Language section titled Creating Type Synonyms with Type Aliases:

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;

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.


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;

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,
            // ...
        }
    }
}

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 = ();

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

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> {
    // ..
}


来源:https://stackoverflow.com/questions/34938429/does-rust-have-an-idiomatic-equivalent-to-f-typedefs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!