Rust编程的泛型

【图书介绍】《Rust编程与项目实战》-CSDN博客

《Rust编程与项目实战》(朱文伟,李建英)【摘要 书评 试读】- 京东图书 (jd.com)

Rust编程与项目实战_夏天又到了的博客-CSDN博客

7.6.1  什么是泛型编程

C/C++、Rust都是强类型语言,在对数据进行处理时,必须明确数据的数据类型。但是很多时候,比如链表这种数据结构,可以是整型数据的链表,也可以是其他类型,可能会写出重复的代码,只是数据类型不同而已。泛型编程是一种编程风格,其中的算法以尽可能抽象的方式编写,而不依赖于将在其上执行这些算法的数据形式。泛型这个词并不是通用的,在不同的语言实现中,具有不同的命名。在Java/Kotlin/C#中称为泛型,在ML/Scala/Haskell中称为Parametric Polymorphism,而在C++中被叫作模板,比如最负盛名的C++中的STL。任何编程方法的发展一定有其目的,泛型也不例外。泛型的主要目的是加强类型安全和减少强制转换的次数。

所以,为了简化代码,我们将类型抽象成一种“参数”,数据和算法针对这种抽象的类型来实现,而不是具体的类型,当需要使用时再具体化、实例化。

7.6.2  在函数中使用泛型

泛型可以在函数中使用,将泛型放在函数的签名中,在其中指定参数的数据类型和返回值。当函数包含类型为T的单个参数时,语法如下:

fn function_name<T>(x:T)  

// body of the function.

上面的语法包含两部分:

  • <T>:给定的函数是一种类型的泛型。
  • (x : T):x是类型T。

当函数包含多个相同类型的参数时,代码如下:

fn function_name<T>(x:T, y:T)  

// body of the function.

当函数包含多个类型的参数时,代码如下:

fn function_name<T,U>(x:T, y:U) 

// Body of the function.

下面举一个泛型的例子:

//不使用泛型
//针对整型数据
fn findmax_int(list : &[i32]) -> i32 {
    let mut max_int = list[0];
    for &i in list.iter() {
        if i > max_int {
            max_int = i;
        }
    }
    max_int
}

//针对char数据
fn findmax_char(list : &[char]) -> char {
    let mut max_char = list[0];
    for &i in list.iter() {
        if i > max_char {
            max_char = i;
        }
    }
    max_char
}
fn main() {
    let v_int = vec![2, 4, 1, 5, 7, 3];
    println!("max_int: {}", findmax_int(&v_int));
    let v_char = vec!['A', 'C', 'G', 'B', 'F'];
    println!("max_char: {}", findmax_char(&v_char));
}

运行结果如下:

max_int: 7

max_char: G

可以看到两个函数基本上是一样的。下面采用泛型的方式来简化代码:

fn find_max<T : PartialOrd + Copy> (list : &[T]) -> T {
    let mut max = list[0];
    for &i in list.iter() {
        if i > max {
            max = i;
        }
    }
    max
}

fn main() {
    let v_int = vec![2, 4, 1, 5, 7, 3];
    println!("max_int: {}", find_max(&v_int));
    let v_char = vec!['A', 'C', 'G', 'B', 'F'];
    println!("max_char: {}", find_max(&v_char));
}

成功运行,运行结果如下:

max_int: 7

max_char: G

其实泛型是一个比较复杂的概念,可能大家还没体会到泛型的好处,我们再来看一个实际开发中经常会碰到的场景。

话说项目经理总是善变的,有一天项目经理告诉我,替客户计算一个圆形的面积。客户要求很简单,半径只会是u8类型。我写了代码如下:

fn area_u8(r: u8) -> u8 {
    r * r
}

fn main() {
    println!("{}", area_u8(3));
}

第二天项目经理又来了,说客户说的不对,半径在某种情况下还会是u16类型。于是我又添加了一个函数:

fn area_u8(r: u8) -> u8 {
    r * r
}

fn area_u16(r: u16) -> u16 {
    r * r
}

fn main() {
    println!("{}", area_u8(3));
    println!("{}", area_u16(10));
}

但第三天、第四天,项目经理又跑来说半径还会是u32、u64类型,甚至还可能是浮点数。我到底要写多少个函数才行!我意识到是时候叫出“超级飞侠”了。不对,是泛型了。泛型,顾名思义,就是广泛的类型,在Rust中,通常使用<T>表示,当然,不一定是T,也可以是A、B、C……

使用泛型并不容易,在这个例子中,我感受到了Rust编译器的强大。我的第一版程序如下:

fn area<T>(r: T) -> T {
    r * r
}

fn main() {
    println!("{}", area(3));
    println!("{}", area(3.2));
}

然后编译器告诉我:

error[E0369]: cannot multiply `T` by `T`
 --> main.rs:2:7
  |
2 |     r * r
  |     - ^ - T
  |     |
  |     T
  |
help: consider restricting type parameter `T`
  |
1 | fn area<T: std::ops::Mul<Output = T>>(r: T) -> T {
  |          +++++++++++++++++++++++++++

error: aborting due to previous error

不能对两个T类型的数做乘法!那我该怎么办?幸亏有泛型的特性与特性绑定。我这样修改:

fn area<T: std::ops::Mul<Output = T> + Copy>(r: T) -> T {
    r * r
}

fn main() {
    println!("{}", area(3));
    println!("{}", area(3.2));
}

终于可以得到正确结果了,运行结果如下:

9

10.240000000000002

回过头来解释一下刚才的过程。泛型指定的是任意类型,但并不是所有类型都能进行乘法运算。因此,我们需要对泛型加以限制。这被称为特性绑定,或泛型约束,意思是只有满足条件(实现了某个特性)的泛型才被允许传到函数中来。

上面的写法无疑使得第一行很长,可读性不好,为此Rust设计了where子句,用来实现泛型    约束:

fn area<T>(r: T) -> T 
where T: std::ops::Mul<Output = T> + Copy
{
    r * r
}

fn main() {
    println!("{}", area(3));
    println!("{}", area(3.2));
}

运行结果如下:

9

10.240000000000002

7.6.3  在结构体中使用泛型

这下足足过了1个月,我都没见到项目经理的身影,直到有一天,项目经理笑意满满地来到我的工位,说上次的程序写得太棒了,客户发现不管什么时候,我的程序都能正常工作。客户对我们公司非常肯定,决定再给我们一个新的项目:计算长方形的面积,此类项目前景非常好,为了便于扩展,最好能抽象成结构体。于是我一气呵成:

use std::ops::Mul;  				// 这么写可以简化代码

struct Rect<T>      				// 为结构体添加泛型
{
    width: T,       				// 宽和高都是泛型
    height: T
}

impl<T> Rect<T> {   				// 为泛型实现方法,impl后也要添加<T>
    fn area(&self) -> T     
    where T: Mul<Output = T> + Copy { 			// 泛型约束
        self.height * self.width
    }
}

fn main() {
    // 整型
    let rect1 = Rect{width:3, height:4};
    println!("{}", rect1.area());

    // 浮点型
    let rect2 = Rect{width:3.5, height:4.3};
    println!("{}", rect2.area());
}

运行结果如下:

12

15.049999999999999

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值