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 ran
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 Result
s, which you can see in the standard library. Something like:
type CardResult<T> = Result<T, Error>;
fn foo() -> CardResult<String> {
// ..
}