Rust 类型操作
类型 特征 泛型
- 允许用户自定义类型,以避免冗余代码
类型 与 特征
类型
- 一组具有给定语义、布局等等
类型 | 值 |
---|---|
u8 | { 0u8, 1u8, ..., 255u8 } |
char | { 'a', 'b', ... '🦀' } |
struct S(u8, char) | { (0u8, 'a'), ... (255u8, '🦀') } |
类型等价与转换
- 这可能很明显,但
u8
,&u8
,&mut u8
,彼此完全不同 - 任何
t: T
仅接受来自准确的T
值,如f(0_u8)
不能被f(&0_u8)
调用f(&mut my_u8)
不能被f(&my_u8)
调用f(0_u8)
不能被f(0_i8)
调用
*在数学意义上,从类型的角度来看 0 != 0
,在语言意义上,==(0u8, 0u16)
仅为了阻止一些小意外而没有定义
类型 | 值 |
---|---|
u8 | { 0u8, 1u8, ..., 255u8 } |
u16 | { 0u16, 1u16, ..., 65_535u16 } |
&u8 | { 0xffaa&u8, 0xffbb&u8, ... } |
&mut u8 | { 0xffaa&mut u8, 0xffbb&mut u8, ... } |
- 然而,Rust 有时可能有助于在类型之间进行转换
- casts手动转换类型的值,
0_i8 as u8
- coercions安全时自动转换类型,
let x: &u8 = &mut 0_u8;
- casts手动转换类型的值,
- casts和coercions强制转换将一个集合(如
u8
)的值转换到另一个集合(如u16
),可能会添加 CPU 指令来完成此操作;这与子类型不同,意味着类型和子类型是同一个集合的一部分(如u8
是u16
的子类型,跟0_u8
和0_u16
类似),而这种转换将纯粹是编译时检查。Rust并不对常规类型使用子类型(0_u8
与0_u16
确实不同),而是对生命周期使用某种类型。 - 这里的安全不仅仅是物理概念(如
&u8
不能被强制转换为&u128
),还包括“历史表明这样的转换是否会导致编程错误”。
Implementations — impl S { }
impl Port {
fn f() { ... }
}
- 类型通常伴随着实现,如
impl Port {}
,与类型相关的行为有:- 相关函数
Port::new(80)
- 方法
port.close()
- 相关函数
*与之相关的更多的是哲学而不是技术,没有什么(除了好的品味)可以阻止 u8::play_sound()
的发生。
特征 — trait T { }
⌾ Copy
⌾ Clone
⌾ Sized
⌾ ShowHex
- Traits ...
- 是“抽象”行为的方式
- trait 作者在语义上声明这个 trait 意味着 X
- 其他人可以为他们的类型实现该行为
- 将 trait 视为类型的“成员列表”:
Copy Trait | Clone Trait | Sized Trait | ||
---|---|---|---|---|
Self | Self | Self | ||
u8 | u8 | char | ||
u16 | String | Port | ||
... | ... | ... | ||
trait 作为成员表,Self 是指包含的类型 |
- 属于该成员名单的任何人都将遵守名单的行为
- Traits 还可以包括相关的方法、函数、...
trait ShowHex {
// Must be implemented according to documentation.
fn as_hex() -> String;
// Provided by trait author.
fn print_hex() {}
}
⌾ Copy
trait Copy { }
- 没有方法的特征通常被称为marker traits
Copy
是标记特征的例子,意味着内存可以按位复制
⌾ Sized
- 完全在明确控制外的一些 traits
- Sized 由已知大小类型的编译器提供,要么是,要么不是
Implementing Traits for Types — impl T for S { }
impl ShowHex for Port { ... }
- 特征是在'at some point'时为类型实现的
impl A for B
添加类型B
到 trait 成员列表:
ShowHex Trait |
---|
Self |
Port |
- 从表面上看,你可以认为该类型因其成员资格而获得“badge”
u8 | Device | Port | ||
---|---|---|---|---|
impl { ... } | impl { ... } | impl { ... } | ||
⌾Sized | ⌾Transport | ⌾Sized | ||
⌾Clone | ⌾Clone | |||
⌾Copy | ⌾ShowHex |
特征与接口
接口
- 在 Java 中,用户Alice创建接口
Eat
- 当用户Bob创建新类型
Venison
,他必须决定是否让新类型实现Eat
- 换句话说,所有成员资格必须在类型定义期间详尽地说明
- 当使用新类型
Venison
时,用户Santa可以利用Eat
提供的以下行为:
// Santa imports `Venison` to create it, can `eat()` if he wants.
import food.Venison;
new Venison("rudolph").eat();
特征
- 在 Rust 中,用户Alice创建特征
Eat
- 用户Bob创建新类型
Venison
,并且决定不去实现Eat
,他可能甚至都不知道Eat
的存在 - 某用户后来决定添加
Eat
到Venison
将是一个非常好的主意 - 当使用
Venison
时,用户Santa必须单独导入Eat
:
// Santa needs to import `Venison` to create it, and import `Eat` for trait method.
use food::Venison;
use tasks::Eat;
// Ho ho ho
Venison::new("rudolph").eat();
*为了防止两个人实现不同的 Eat
,Rust 编译器将限制选择为 Alice 或 Bob 其中的一个, 也就是说 impl Eat for Venison
可能只发生在 Venison crate 或 Eat crate 中。