Rust泛型编程怎么用?从圆形面积计算到结构体实战详解

 更新时间:2026年10月18日 10:25:53   作者:swanmy  
还在为Rust泛型头疼吗?本文通过计算圆形和长方形面积的实际案例,手把手教你如何使用泛型、特性绑定(如Mul和Copy)以及where子句,并解决结构体中整数与浮点数混合运算的难题,看完你就能轻松应对项目经理的善变需求

在函数中使用泛型

项目经理总是善变的,有一天项目经理告诉我,替客户计算一个圆形的面积。

客户要求很简单,半径只会是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` to `T`
 --> src\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类型的数做乘法!那我该怎么办?

特性与特性绑定

别急,看第11行。它直接告诉我应该这么写:fn area<T: std::ops::Mul<Output = T>>(r: T) -> T {,于是我的第二版程序诞生了:

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

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

再编译:

error[E0382]: use of moved value: `r`
 --> src\main.rs:2:9
  |
1 | fn area<T: std::ops::Mul<Output = T>>(r: T) -> T {
  |                                       - move occurs because `r` has type `T`, which does not implement the `Copy` tr
ait
2 |     r * r
  |     -   ^ value used here after move
  |     |
  |     value moved here
  |
help: consider further restricting this bound
  |
1 | fn area<T: std::ops::Mul<Output = T> + Copy>(r: T) -> T {
  |                                      ^^^^^^

error: aborting due to previous error

还是不对,在第14行编译器又告诉我应该这么写,于是,第三版:

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

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

终于可以得到正确结果了。

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

第二版的<T: std::ops::Mul<Output = T>>就是约束只有实现了std::ops::Mul特性的泛型才可以通过编译器的检查,而在这个例子中,因为r是泛型,在进行乘法时所有权发生了转移,还要进行第二个约束,T必须同时实现std::ops::Mul和Copy两个特性。Rust中可以使用“+”来实现多重约束,这就是<T: std::ops::Mul<Output = T> + Copy>的来历。

上面的写法无疑使得第一行很长,可读性不好,为些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));
}

在结构体中使用泛型

这下足足过了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());
}

正在洋洋自得时,测试MM微笑着来到我的面前。我以为要步入人生的新阶段时,测试MM开口了:“你这个函数不对啊,如果宽和高一个是整数一个是浮点数……”我瞬间石化,后面的我再也听到。

也不知道测试MM什么时候走的,回过神来的我马上试验:

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

struct Rect<T, U>   // 为结构体添加两个泛型
{
    width: T,       // 宽和高是不同的泛型
    height: U
}

impl<T, U> Rect<T, U> {   // 为泛型实现方法,impl后也要添加<T>
    fn area(&self) -> T     
    where T: Mul<U, Output = T> + Copy,     // Mul的第一个参数,表示让这个类型和自身相乘,Output表示输出值的类型
          U: Mul<T, Output = T> + Copy {       
        self.height * self.width
    }
}

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

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

编译器报错:

error[E0277]: cannot multiply `{float}` to `{integer}`
  --> src\main.rs:20:26
   |
20 |     println!("{}", rect1.area());
   |                          ^^^^ no implementation for `{integer} * {float}`
   |
   = help: the trait `std::ops::Mul<{float}>` is not implemented for `{integer}`

error[E0277]: cannot multiply `{integer}` to `{float}`
  --> src\main.rs:20:26
   |
20 |     println!("{}", rect1.area());
   |                          ^^^^ no implementation for `{float} * {integer}`
   |
   = help: the trait `std::ops::Mul<{integer}>` is not implemented for `{float}`

error[E0277]: cannot multiply `{integer}` to `{float}`
  --> src\main.rs:24:26
   |
24 |     println!("{}", rect2.area());
   |                          ^^^^ no implementation for `{float} * {integer}`
   |
   = help: the trait `std::ops::Mul<{integer}>` is not implemented for `{float}`

error[E0277]: cannot multiply `{float}` to `{integer}`
  --> src\main.rs:24:26
   |
24 |     println!("{}", rect2.area());
   |                          ^^^^ no implementation for `{integer} * {float}`
   |
   = help: the trait `std::ops::Mul<{float}>` is not implemented for `{integer}`

error: aborting due to 4 previous errors

Rust竟然不能把整型和浮点数相乘,这就不得不吐槽一下Rust了,因为Rust不像C语言那样有隐式类型转换,C语言遇到这样的问题时,会把两个值都转换成double来计算,而Rust不会。编译器告诉我,要想实现这个问题,需要使用From和Into特性,我查了很多资料,加群问了大佬,也只能写出来第一个参数是浮点第二个参数是整数的解决方案。

use std::ops::Mul;  // 这么写可以简化代码
use std::convert::{Into};
struct Rect<T, U>   // 为结构体添加两个泛型
{
    width: T,       // 宽和高是不同的泛型
    height: U
}

impl<T, U> Rect<T, U> {   // 为泛型实现方法,impl后也要添加<T>
    fn area(&self) -> T     
    where T: Mul<Output = T> + Copy,     // Mul的第一个参数,表示让这个类型和自身相乘,Output表示输出值的类型
          U: Into<T> + Copy {
        self.width.mul(self.height.into())
    }
}

fn main() {
    let rect2 = Rect{width:3.1, height:4};
    println!("{}", rect2.area());
}

这一点让刚学Rust的我很是郁闷,换成别的语言,都是很简单的事件,怎么在Rust里这么难。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 详解thiserror库在Rust中的使用

    详解thiserror库在Rust中的使用

    在编程中,错误处理是一个至关重要的部分,在Rust中,我们经常使用Result和Option类型来进行错误处理,但有时,我们需要创建自定义的错误类型,这就是thiserror库发挥作用的地方,可以极大的简化代码,所以本文就给大家介绍一下如何使用thiserror
    2023-08-08
  • Rust综合实践之模拟cmd的文件操作过程

    Rust综合实践之模拟cmd的文件操作过程

    想用Rust打造自己的命令行工具却不知从何下手?本文带你从零实现ls、touch、cat等基础命令,详解文件读写、目录遍历和错误处理,附完整代码,助你快速掌握Rust系统编程核心技能
    2026-10-10
  • Rust搭建webserver的底层原理与应用实战技巧

    Rust搭建webserver的底层原理与应用实战技巧

    本文介绍Rust在HTTP编程中的应用,涵盖协议基础、标准库与第三方库(如hyper、reqwest)的使用,以及多线程服务器和线程池的实现与关闭机制,展示Rust在性能、安全和并发方面的优势,感兴趣的朋友跟随小编一起看看吧
    2025-06-06
  • Rust 原始指针功能探索

    Rust 原始指针功能探索

    这篇文章主要为大家介绍了Rust 原始指针功能探索,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • Rust错误处理机制详解:如何优雅处理不可恢复与可恢复错误

    Rust错误处理机制详解:如何优雅处理不可恢复与可恢复错误

    还在为Rust的错误处理头疼吗?本文深入讲解Rust如何通过Result枚举和panic宏区分可恢复与不可恢复错误,并介绍unwrap、expect和?运算符的实用技巧,帮你写出更健壮的代码
    2026-10-10
  • Rust结构体的定义与实例化详细讲解

    Rust结构体的定义与实例化详细讲解

    结构体是一种自定义的数据类型,它允许我们将多个不同的类型组合成一个整体。下面我们就来学习如何定义和使用结构体,并对比元组与结构体之间的异同,需要的可以参考一下
    2022-12-12
  • MacBook Pro安装rust编程环境的过程

    MacBook Pro安装rust编程环境的过程

    rustup是一个用于管理Rust版本和工具链的工具,这篇文章主要介绍了MacBook Pro安装rust编程环境的过程,感兴趣的朋友跟随小编一起看看吧
    2024-02-02
  • rust 自定义迭代器的实现方法

    rust 自定义迭代器的实现方法

    本文介绍了Rust中的迭代器概念、IteratorTrait及其实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2026-01-01
  • Rust 模式匹配示例详解

    Rust 模式匹配示例详解

    这篇文章主要为大家介绍了Rust 模式匹配示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • Rust语言之trait中的个方法可以重写吗

    Rust语言之trait中的个方法可以重写吗

    在Rust中,trait定义了一组方法,这些方法可以被一个或多个类型实现,当你为某个类型实现一个trait时,你可以为该trait中的每个方法提供自己的具体实现,本文将给大家介绍一下trait中的个方法是否可以重写,需要的朋友可以参考下
    2023-10-10

最新评论