泛型类型是减少代码重复的好方法,因此它们对性能有巨大的影响,通过利用Rust中的泛型类型,开发人员可以编写更通用和可重用的代码,同时保持类型安全和性能。这种方法不仅减少了冗余,还增强了代码的可维护性和可扩展性,还可以促进更清晰、更灵活的设计,使开发人员能够轻松地适应不断变化的需求。
Introduction 介绍
Generic types in Rust allow you to write functions, structs, and enums that can operate on multiple data types without sacrificing type safety, enabling code reuse and flexibility. Let’s take a look at a use case.
Rust中的泛型类型允许您编写可以对多种数据类型进行操作的函数,结构和枚举,而不会牺牲类型安全性,从而实现代码重用和灵活性。让我们来看看一个用例。
fn find_max(x: i32, y: i32) -> i32{if x > y {x}else{y}
}fn main(){let result: i32 = find_max(9,10);println!("The maximum value is : {}", result);
}
Output: 输出量:
The maximum value is 10
If I wanted to use the same function for characters or floating point numbers, I would have to create a new function with the specific types with basically the same logic. Generic type helps to reduce this code duplication.
如果我想对字符或浮点数使用相同的函数,我必须创建一个具有基本相同逻辑的特定类型的新函数。泛型类型有助于减少这种代码重复。
We can declare multiple generic types for a function/struct. Single generic types are generally denoted by T
which stands for Type
.
我们可以为一个函数/结构声明多个泛型类型。单个泛型类型通常用 T
表示,它代表 Type
。
Generic types are declare within <>
in a function.
泛型类型在函数中的 <>
中声明。
Example : 范例:
fn find_max<T: PartialOrd>(x: T, y: T) -> T {if x > y {x} else {y}
}fn main() {let result = find_max(10, 5);println!("The maximum value is: {}", result);let result_float = find_max(4.5, 3.2);println!("The maximum value is: {}", result_float);let result_char = find_max('x','a');println!("The maximum value is: {}", result_char);
}
Output: 输出量:
The maximum value is: 10
The maximum value is: 4.5
The maximum value is: x
Explanation : 说明:
- - The
find_max
function is defined with the generic type argumentT
.
-find_max
函数是用泛型类型参数T
定义的。 - The
<T: PartialOrd>
syntax indicates thatT
must implement thePartialOrd
trait, which allows for comparisons across values of typeT
.<T: PartialOrd>
语法表明T
必须实现PartialOrd
trait,这允许在类型T
的值之间进行比较。 PartialOrd
is a trait in Rust that defines partial ordering relations (less than, less than or equal to, greater than, greater than or equal to) between values of the same type.PartialOrd
是Rust中的一个trait,它定义了相同类型的值之间的偏序关系(小于,小于或等于,大于,大于或等于)。- Types implementing
PartialOrd
can be compared with the comparison operators (<
,<=
,>
,>=
) and can be used in functions that rely on ordering, like sorting algorithms or in this case, finding the maximum value.
实现PartialOrd
的类型可以与比较运算符(<
,<=
,>
,>=
)进行比较,并且可以用于依赖于排序的函数中,例如排序算法或在本例中查找最大值。 - This allows the function to work with a variety of types as long as they support comparison using the
PartialOrd
trait.
这允许函数处理各种类型,只要它们支持使用PartialOrd
trait进行比较。 - By utilizing generics, the function gets flexibility and reusability while maintaining type safety.
通过使用泛型,函数在保持类型安全的同时获得了灵活性和可重用性。 - The function returns a value of type
T
, guaranteeing that the maximum value is the same as the input values.
该函数返回一个类型为T
的值,保证最大值与输入值相同。
Generic types in Structs 结构中的泛型类型
Let’s say I want to store a co-ordinates of a point. Here’s a struct that I would normally use
假设我想存储点的坐标。这里有一个我通常会使用的结构
struct Point{x: i32,y: i32,
}fn main(){let point = Point{x: 3, y: 10};//This line will not work because out struct can only store signed 32 bit integerslet point2 = Point{x:3.4, y: 6.9};
}
Let’s fix this issue: 让我们解决这个问题:
struct Point<T>{x: T,y: T,
}fn main(){let point = Point{x: 3, y: 10};let point2 = Point{x:3.4, y: 6.9};//This line will not work because out struct can not store two different data typeslet point3 = Point{x:3.4, y: 6};
}
To fix this, we can add another generic type in out struct.
为了解决这个问题,我们可以在out结构中添加另一个泛型类型。
struct Point<T, U>{x: T,y: U,
}fn main(){let point = Point{x: 3, y: 10};let point2 = Point{x:3.4, y: 6.9};let point3 = Point{x:3.4, y: 6};
}
Now all of this works, and that’s how we can use Generic types in structs…
现在所有这些都可以工作了,这就是我们如何在结构中使用泛型类型。
Generic types in enums 枚举中的泛型类型
We can do the same with enums, I’ll show you an example of two most popular enums with generic types:
我们可以对枚举做同样的事情,我将向你展示两个最流行的泛型枚举的例子:
enum Option<T>{Some(T),None,
}
enum Result<T,E>{Ok(T),Err(E),
}
Generic types in methods 方法中的泛型类型
If you recall, We can use impl
to declare implementation blocks for a struct to declare methods for a struct.
如果你还记得的话,我们可以使用 impl
来声明结构的实现块来声明结构的方法。
Getting back to our Point struct, Let’s declare an implementation block using generic types for the struct.
回到我们的Point结构,让我们使用结构的泛型类型声明一个实现块。
enum Result<T,E>{Ok(T),Err(E),
}
The method X retrurns the X co-ordinate of the point taking the reference of self
方法X返回以 self
为参考的点的X坐标
Notice how while declaring the struct is used T
as the generic type and in the impl
block I am using U
this shows that both of these generic types are not tied together.
请注意,当声明结构体时,我使用 T
作为泛型类型,而在 impl
块中,我使用 U
,这表明这两个泛型类型没有绑定在一起。
Now what will happpen if I declare another impl
block with a concrete type?
现在,如果我用一个具体类型声明另一个 impl
块,会发生什么?
struct Point<T>{x: T,y: T,
}impl<U> Point<U>{fn x(&self) -> &U {&self.x}
}impl Point<i32>{fn y(&self) -> i32{self.y}
}fn main(){let point1 = Point{x: 3, y: 10};let point2 = Point{x:3.4, y: 6.9};println!("X: {}, Y: {}",point1.x(),point1.y());println!("X: {}, Y: {}",point2.x(),point2.y);
}
The method y
is only available to the instances which have the x
and y
values as signed 32 bit integers, whereas the x
method can be used on the instances of any type.
方法 y
仅适用于具有作为有符号32位整数的 x
和 y
值的实例,而方法 x
可用于任何类型的实例。
Mixing up generics 混淆泛型
Let’s take a look at a more complex example where we had two generics in out Point struct and understand the need of mixing them up…
让我们来看看一个更复杂的例子,我们在out Point结构中有两个泛型,并理解混合它们的必要性。
struct Point<T, U>{x: T,y: U,
}impl<T,U> Point<T,U>{fn mixup<V, W>(self,other: Point<V, W>) -> Point<V, W>{Point{x: self.x,y: other.y,}}
}fn main(){let point = Point{x: 3, y: 10};let point2 = Point{x:3.4, y: 6.9};let point3 = Point{x:3.4, y: 6};let point4 = point1.mixup(point2);println!("X: {}, Y: {}",point4.x, point4.y);
}
- The code defines a generic struct called
Point<T, U>
, which represents a point in a 2D space. It has two fieldsx
andy
, each with its own generic typeT
andU
respectively.
代码定义了一个名为Point<T, U>
的通用结构,它表示2D空间中的一个点。它有两个字段x
和y
,每个字段分别有自己的泛型类型T
和U
。 - An implementation block (
impl
) for thePoint<T, U>
struct is provided. Inside this block, there's a methodmixup<V, W>(self, other: Point<V, W>) -> Point<V, W>
. This method takes anotherPoint
instance as an argument with potentially different types (V
andW
) for itsx
andy
values.
提供了Point<T, U>
结构的实现块(impl
)。在这个块中,有一个方法mixup<V, W>(self, other: Point<V, W>) -> Point<V, W>
。此方法将另一个Point
实例作为参数,其x
和y
值可能具有不同的类型(V
和W
)。 - Inside the
mixup
method, a newPoint
instance is created with thex
value from the currentPoint
instance (self.x
) and they
value from theother
Point
instance (other.y
). Bothx
andy
values are of the generic typesV
andW
respectively.
在mixup
方法内部,使用来自当前Point
实例(self.x
)的x
值和来自other
Point
实例(other.y
)的y
值创建新的Point
实例。x
和y
值分别是泛型类型V
和W
。 - In the
main
function: 在main
函数中: - Four
Point
instances are created:point
,point2
,point3
, andpoint4
.
创建了四个Point
实例:point
、point2
、point3
和point4
。 point4
is created by calling themixup
method onpoint1
(of typePoint<i32, i32>
) withpoint2
(of typePoint<f64, f64>
). This demonstrates mixing up two different generic types.point4
是通过使用point2
(类型Point<f64, f64>
)调用point1
(类型Point<i32, i32>
)上的mixup
方法创建的。这演示了两种不同泛型类型的混合。- Finally, the
x
andy
values ofpoint4
are printed to the console.
最后,将point4
的x
和y
值打印到控制台。
Why mix up generics? 为什么要混淆泛型?
V
and W
are like placeholders for types. We use them in the mixup
method because it lets us mix and match different types for the x
and y
values of two Point
instances. This flexibility makes our code more versatile because it allows us to work with different types without having to change the struct itself. So, V
and W
are used instead of T
and U
to keep things clear and flexible in the method.V
和 W
就像类型的占位符。我们在 mixup
方法中使用它们,因为它允许我们混合和匹配两个 Point
实例的 x
和 y
值的不同类型。这种灵活性使我们的代码更通用,因为它允许我们使用不同的类型,而不必改变结构本身。因此,使用 V
和 W
而不是 T
和 U
来保持方法的清晰和灵活性。