Rust 的类型系统
类型是一组具有共同特点的值的集合。不同的类型将值划分到不同的集合,每个类型都定义了它所包含的值在内存中的大小及其所能进行的操作。而类型系统则是,对类型进行定义、检查和处理的系统。
Rust中的类型包括原生类型和组合类型。原生类型是编程语言提供的最基础的数据类型。比如字符、整数、浮点数、布尔值、数组、元组、指针、引用、函数、闭包等。所有原生类型的大小都是固定的,因此它们可以被分配到栈上。组合类型或者说复合类型,是指由一组原生类型和其它类型组合而成的类型。组合类型又可以分为以下两类:
– 结构体
– 标签联合(不相交并集)
结构体是多个类型组合在一起共同表达一个值的复杂数据结构。标签联合可以存储一组不同但固定的类型中的某个类型的对象,具体是哪个类型由其标签决定。
//结构体
struct Student {
name: String,
age: u8,
}
//标签联合
struct Ipv4Addr {
// --snip--
}
struct Ipv6Addr {
// --snip--
}
enum IpAddr {
V4(Ipv4Addr),
V6(Ipv6Addr),
}
按定义后类型是否可以隐式转换,可以分为强类型和弱类型。Rust 不同类型间不能自动转换,所以是强类型语言。
按类型检查的时机,在编译时检查还是运行时检查,可以分为静态类型系统和动态类型系统。Rust的类型系统属于静态类型系统。
所谓类型安全,是指代码,只能按照被允许的方法,访问它被授权访问的内存。
Rust 中除了 let / fn / static / const 这些定义性语句外,都是表达式,而一切表达式都有类型。
在 Rust 中,对于一个作用域,无论是 if / else / for 循环,还是函数,最后一个表达式的返回值就是作用域的返回值,如果表达式或者函数不返回任何值,那么它返回一个 unit() 。unit 是只有一个值的类型,它的值和类型都是 () 。
if has_work {
do_something();
}
上面这个if块的类型即()。
类型系统中的多态是一种重要的思想,它是指在使用相同的接口时,不同类型的对象,会采用不同的实现。Rust中,多态分为以下三种:
– 参数多态
– 特设多态
– 子类型多态
参数多态是指将代码中所涉及的类型作为参数,而不是某一个具体的类型;特设多态是指对于同一个行为,具有不同的实现;子类型多态是指,在运行时,子类型可以被当作父类型使用。在 Rust 中,参数多态通过泛型来支持、特设多态通过 trait 来支持、子类型多态可以用 trait object 来支持。
下面是一个范型的例子:
fn add<T: std::ops::Add<Output = T>>(a:T, b:T) -> T {
a + b
}
fn main() {
println!("add i8: {}", add(2i8, 3i8));
println!("add i32: {}", add(20, 30));
println!("add f64: {}", add(1.23, 1.23));
}
其中T被称为范型参数。要使用泛型参数,必需在使用前对其进行声明:
fn largest<T: std::ops::Add<Output = T>>(list: &[T]) -> T {
其中,std::ops::Add 表示范型参数T所要满足的trait。
Trait可以理解为特型或者说特征。如果不同的类型具有相同的行为,那么我们就可以定义一个特征,然后为这些类型实现该特征。定义特征是把一些方法组合在一起,目的是定义一个实现某些目标所必需的行为的集合。下面是定义一个trait的例子:
pub trait Summary {
fn summarize(&self) -> String;
}
trait只定义行为看起来是什么样的,而不定义行为具体是怎么样的。trait可以理解为面向对象程序设计中的接口。
Rust中为类型实现行为要用到impl关键字。对于非范型类型:
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
}
对于范型类型,需要在impl后面加上范型参数:
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
fn main() {
let p = Point { x: 5, y: 10 };
println!("p.x = {}", p.x());
}
对于trait,需要用到for关键字,表明为谁而实现:
pub trait Summary {
fn summarize(&self) -> String;
}
pub struct Post {
pub title: String, // 标题
pub author: String, // 作者
pub content: String, // 内容
}
impl Summary for Post {
fn summarize(&self) -> String {
format!("文章{}, 作者是{}", self.title, self.author)
}
}
pub struct Weibo {
pub username: String,
pub content: String
}
impl Summary for Weibo {
fn summarize(&self) -> String {
format!("{}发表了微博{}", self.username, self.content)
}
}
可以在trait中定义具有默认实现的方法,这样其它类型无需再实现该方法:
pub trait Summary {
fn summarize(&self) -> String {
String::from("(Read more...)")
}
}
trait可以作为函数参数:
pub fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
其中impl Summary表示所有实现Summary trait的类型。
它的完整形式如下:
pub fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
这被称之为特征约束。
也可以利用where关键字进行特征约束:
fn some_function<T, U>(t: &T, u: &U) -> i32
where T: Display + Clone,
U: Clone + Debug
{
trait作为函数返回值:
fn returns_summarizable() -> impl Summary {
Weibo {
username: String::from("sunface"),
content: String::from(
"m1 max太厉害了,电脑再也不会卡了",
)
}
}
特征对象?
todo……