看一看Rust,记录笔记:类型系统

文章目录

  • 第五章:类型系统
    • 泛型
      • 泛型与容器
      • 泛型与结构体
      • 泛型与枚举
      • 泛型与函数
      • 泛型与方法
    • trait系统
      • trait 定义与实现
      • trait 作为参数
        • impl Trait
        • trait 约束
      • 返回实现trait 的类型
      • 标准版常用trait
        • 格式化输出Debug 和Display
        • 等值比较Eq与PartialEq
        • 次序比较Ord 与 PartialOrd
        • 复制值 Clone 与 Copy
        • 默认值Default
      • 类型转换
        • 原生类型间的转换
        • 数字与String 类型间的转换
        • &str 与 String 类型间转换

第五章:类型系统

Rust 是一门强类型且类型安全的静态语言,Rust 中一切皆类型,包括基本的原生类型和复合类型。

这里重点介绍 泛型 和 trait 系统

泛型

泛型是指在运行时指定数据类型的机制,优势是可以编写更为抽象和通用的代码,减少重复的工作量

表示为 泛型,,

泛型与容器

fn main() {
    let mut vec_integer: Vec<i32> = vec![1,2];
    vec_integer.push(3);
}

泛型与结构体

泛型类型的结构体是指结构体的字段类型是泛型类型,他可以拥有一个或多个泛型类型。

struct Rectangle1<T> {
    width: T,
    height: T,
}

struct Rectangle2<T, U> {
    width: T,
    height: U,
}

impl<T> Rectangle1<T> {
    fn width(&self) -> &T {
        &self.width
    }
    fn height(&self) -> &T {
        &self.height
    }
}

impl Rectangle1<i32> {
    fn area(&self) -> i32 {
        self.width * self.height
    }
}


impl<T, U>  Rectangle2<T, U> {
    fn width(&self) -> &T {
        &self.width
    }

    fn height(&self) -> &U {
        &self.height
    }
}

fn main() {
    let rect1 = Rectangle1 { width: 8, height: 2 };
    println!("rect1.width: {}", rect1.height);

    println!(rect1.area());


    let rect2 = Rectangle2 { width: 8, height: 2.2 };
    print!(rect2.width);
}

泛型与枚举

泛型枚举是指枚举值类型是泛型类型。标准库提供的Option 就是一个应用广泛的泛型枚举

enum Option<T>{
    Some(T),
    None,
}

Option 表示可能有值,也可能无值这一抽象概念,

Some 表示可能的值可以使任意类型T,

None 表示不存在Option类型会被Rust自动引入,不需要再显示引用

fn option_add(x: Option<i32>, y: Option<i32>) -> Option<i32> {
    return if x.is_none() && y.is_none() { None } else if x.is_none() { y } else if y.is_none() { x } else {
        Some(x.unwrap() + y.unwrap()) // 如果值为None, 不能使用unwrap
    };
}

泛型与函数

函数的参数和返回值都可以是泛型类型,带有泛型类型的参数或返回值的函数叫做泛型函数。

fn foo<T>(x: T) -> T {
    return x;
}

fn main() {
    println!("{}", foo(4));
    println!("{}", foo("hello"))
}

泛型与方法

带有泛型类型的参数或返回值的方法叫作泛型方法

需要在impl后面跟着 才可以在方法的参数或返回值使用泛型

impl<T> Rectange1<T>{
	fn width(&self) -> &T{
    	&self.width
    }
}

trait系统

Rust 中没有 接口这样的概念。trait 是 唯一的接口抽象方式,用于跨多个结构体以一种抽象的方式定义共享的行为(方法),即 trait 可以让不同的结构体实现相同的行为。

trait 定义与实现

trait 的本质 是一组方法原理,是实现某些目的的定位集合。

trait Geometry{
	fn area(&self)-> f32;
    fn perimeter(&sefl)->f32;
}

从语法来说,trait 可以包含两种形式的方法: 抽象方法和具体方法。

trait Geometry {
    fn area(&self) -> f32;
    fn perimeter(&self) -> f32;
}

struct Rectangle {
    width: f32,
    height: f32,
}

impl Geometry for Rectangle {
    fn area(&self) -> f32 {
        self.width * self.height
    }

    fn perimeter(&self) -> f32 {
        (self.width + self.height) * 2
    }
}

struct Circle {
    radius: f32,
}

impl Geometry for Circle {
    fn area(&self) -> f32 {
        3.14 * self.radius * self.radius
    }

    fn perimeter(&self) -> f32 {
        3.14 * 2.0 * self.radius
    }
}

fn main() {
    let rect = Rectangle { width: 10.0, height: 20.0 };
    println!("rect : area :{}", rect.area())
}

trait 作为参数

trait 作为参数的两种常用方式:

  1. 使用impl Trait 语法表示参数类型
  2. 使用trait 对泛型参数进行约束

impl Trait

// 这里没有实现Geometry trait ,在调用print 函数,如果向其传递String或者i32 会报错 

fn print(geometry: impl Geometry) {
    println!("area : {}", geometry.area());
}

fn main() {
    let rect = Rectangle { width: 10.0, height: 20.0 };
    println!(rect)
}

下面是实现了Geometry trait 和 Display trait

use std::fmt::{Display, Formatter, Result};

impl Dispaly for Rectangle {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        write!(f, "Rectangle:({},{})", self.width, self.height)
    }
}

fn print1(geometry: impl Geometry + Display) {   // 表示同时实现 两种trait
    println!("{},area:{}", geometry, geometry.area())
}

fn main() {
    let rect = Rectangle { width: 10.0, height: 5.4 };
    println!(rect)
}

impl 还支持多个参数指定类型,

fn area_add(geo1: impl Geometray,geo2:impl Geometry){
	println!("{}",geo1)
}

trait 约束

约束是指使用trait 对泛型进行约束。

语法是:

fn generic(t:T){}

impl Display for Circle {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        write!(f, "Circle:({})", self.radius)
    }
}

fn print2<T: Geometry + Display>(geometry: T) {
    println!("{},area : {}", geometry, geometry.area())
}

fn main() {
    let circle = Circle { radius: 3.0 };
    print2(circle)
}

这里跟impl trait 相似的是,也是可以有多个trait 约束,

fn area_add<T:Geometry+Display +Clone,U:Geometry+Display+Debug>(geo1:T,geo2:U){}

在上述过于长的约束,会导致函数签名阅读性变差,,

fn area_add<T,U> (geo1:T,geo2:U) where T: Geometry+Display+Clone,U:Geometry+Dispaly+Debug{}

返回实现trait 的类型

函数的返回值类型也可以使用impl Trait 语法,返回某个实现了trait 的类型,

fn return_geomery() -> impl Geometry{
	Rectangle{
    	width:12.5,
        height: 5.5,
    }
}

标准版常用trait

trait 可以应用于填结构体或枚举定义的derive属性中

例如: 对于#[derive]语法标记的类型,编译器会自动为其生成对应trait的默认实现代码。

格式化输出Debug 和Display

Debug trait 可以开启格式化字符串中的调试格式,常用于调试上下文中以{:?}{:#?}格式打印输出一个类型的实例。

Display trait 是以{}格式打印输出信息的,主要用于面向用户的输出。但是Display不能与derive 属性一起使用。

要实现Display 必须实现fmt方法

// 打印类型实例
#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

impl Display for Point {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        write!(f, "({},{})", self.x, self.y)
    }
}

fn main() {
    let origin = Point { x: 0, y: 0 };
    println!("{}", origin);
    println!("{:?}", origin);
    println!("{:#}", origin)
}

(0,0)
Point (x:0,y:0)
Point {
	x:0,
    y:0
}

等值比较Eq与PartialEq

Eq 和 Partial 都是来自数学的等价关系和局部等价关系。两者都满足以下两个特性:

  1. 对称性,即 a==b 可推导出 b == a
  2. 传递性 , 即 a ==b 且 b c 可推导出 a c
  3. eq 特殊: 反身性 : 即 a== a
  4. 浮点数类型,两个非数字值 ,特殊 NaN != NaN>

PartialEq 可以通过判断结构体的参数是否相等,来判断实例是否相等。(这里书中并没有说地址,而是实例里面的字段)

enum BookFormat {
    Paperback,
    // Hardback,
    Ebook,
}
// 新版rust 不再容许创建,而不使用的情况
#[allow(dead_code)]
struct Book {
    isbn: i32,
    format: BookFormat,
}

impl PartialEq for Book {
    fn eq(&self, other: &Self) -> bool {
        self.isbn == other.isbn
    }
}


fn main() {
    let b1 = Book { isbn: 3, format: BookFormat::Paperback };
    let b2 = Book { isbn: 3, format: BookFormat::Ebook };
    let b3 = Book { isbn: 5, format: BookFormat::Paperback };


    assert!(b1 == b2);
    assert!(b1 != b3);
}

次序比较Ord 与 PartialOrd

Ord 时表示 全序关系的trait ,全序关系 是指集合中任何一对元素都是相互可比较。以下特性:

  1. 完成反对称,即任何一对元素之间的关系只能是a b 中的其中一种
  2. 传递性,即 a < b 且 b < c 可推导出 a 同理。

partialOrd 基于排序目的对类型实例进行比较的,可以直接使用 <,>,<= 和 >= 运算符进行比较。特性:

  1. 反对称性 a b) ,反之亦然
  2. 传递性, 即 a 同理

两者都要求能进行元素是否相等的比较,因此对Eq 和 PartialEq 有以下依赖要求:

  1. PartialOrd 要求类型实现 PartialEq
  2. Ord 要求类型实现 partialOrd 和 Eq

复制值 Clone 与 Copy

这里涉及 栈内存 和堆内存 ,按位复制和深复制以及引用概念

Clone trait 用于标记 可以对值进行深复制的类型,即对栈上和堆上的数据一起复制。

实现CLone,需要实现clone方法。如果使用 trait -》 #[derive(Clone)] 标记结构或者枚举,必须要求结构体每个字段或枚举都可调用clone 方法。换句话说 都要必须实现Clone 。

Copy trait 用于标记 按位复制其值的类型,即复制栈上的数据。且必须实现Clone 的 clone 方法。简化方法->

#[derive(Copy,Clone)]

等于

impl Copy for mystruct {}

impl Clone for myStruct {

​ fn clone () …

}

Copy 是一个隐式行为,开发者不能重载Copy 行为,就是一个简单的位复制。

常发生 在 执行变量绑定,函数参数传递,函数返回的场景中。

默认值Default

Default trait 为类型提供有用的默认值,通常用于结构体的字段提供默认值。如果每个字段都实现了Default ,那么Default 可以与 derive 属性一起使用,对每个字段都用默认值


#[derive(Default, Debug)]
struct MyStruct {
    foo: i32,
    bar: f32,
}


fn main() {
    let options1: MyStruct = Default::default();

    println!("options: {:?}", options1)
}

类型转换

类型转换 分为隐式转换 和显示类型转换 。

隐式类型转换有编译器来完成的

显示类型是有开发者来指定的。

原生类型间的转换

as 关键字 用于rust 中 原生数据类型间的转换,需要注意的是 , 短类型转换为长类型是没有的,但是长类型转换为短类型会被截断处理。有无符号,也不适合as关键字

这个与每个语言都这相似的规则

fn main() {
    let x: u16 = 7;
    let y = x as u32;
    println!("u16 : {}, u32 : {}", x, y);
    
    
    
}

数字与String 类型间的转换

使用to_string 方法可以将任意数字转换为String 类型,使用 parse 方法可以将 String 类型解析为指定的数字类型

  let x = 7;
    let y = x.to_string();

    let x1 = String::from("8");
    let y1 = x1.parse::<f64>().unwrap();

&str 与 String 类型间转换

  1. 使用 as_str 方法可以将 String 类型转换为&str 类型
  2. 使用to_string 方法可以将 &str 转换为String 类型

你可能感兴趣的:(rust,rust)