【发布时间】:2016-04-28 13:33:16
【问题描述】:
我正在用 Rust 1.6 重写我的现有代码,我发现在源语言中通过 typedef 标记类型非常方便。例如,在我的纸牌游戏中,我在 F# 中定义为:
type Rank = uint8
【问题讨论】:
标签: types rust type-alias
我正在用 Rust 1.6 重写我的现有代码,我发现在源语言中通过 typedef 标记类型非常方便。例如,在我的纸牌游戏中,我在 F# 中定义为:
type Rank = uint8
【问题讨论】:
标签: types rust type-alias
来自The Rust Programming Language 标题为Creating Type Synonyms with Type Aliases 的部分:
Rust 提供了声明 类型别名 的能力,从而为现有类型提供另一个名称。为此,我们使用type 关键字。例如,我们可以像这样创建别名Kilometers 到i32:
type Kilometers = i32;
现在,别名Kilometers 是i32 的同义词; [...],Kilometers 不是一个单独的新类型。 Kilometers 类型的值将被视为与 i32 类型的值相同:
type Kilometers = i32;
let x: i32 = 5;
let y: Kilometers = 5;
println!("x + y = {}", x + y);
您应该阅读更多内容,但这回答了问题。
作为一点编辑,我认为类型别名在人们使用它们的许多地方并不适合。假设您的Rank 类型代表与一副纸牌有关的内容,我建议使用enum 或newtype。原因是使用类型别名可以执行以下操作:
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<Rank, Error>,但后来认为这会令人困惑。 :-)
我使用它们的另一种情况是缩短我不想隐藏的较大类型。像迭代器或Results 这样的类型会发生这种情况,您可以使用see in the standard library。比如:
type CardResult<T> = Result<T, Error>;
fn foo() -> CardResult<String> {
// ..
}
【讨论】:
Rank / 5 或将Rank 传递给采用u8 的函数是否有意义?因为类型别名允许您这样做(因为Rank 是 u8)。当你有一个非常长和复杂的类型并且想要更简洁地引用它时,你通常在 Rust 中使用类型别名是为了方便(但别名 是 类型,应该用来代替它无处不在)