昨天我们说了下rust
推荐的使用panic
的场景
今天该学第十章了。
前面已经多次提到过泛型了,另外在别的语言中你应该也有接触过,比如typescript
。
在rust
中,泛型可以用于function
、struct
以及enum
等中。我们先来学下如何创建相应的泛型。
我们直接来看下例子
规范是放在函数名字之后()
之前 ,用<>
包裹中间的泛型,一般都是用缩写的大写字母。如果需要多个泛型,可以用,
隔开。比如``。
好像没什么用?在这个方法中确实没什么用,不过我们可以来看下一个例子
稍微有那么一点长。
我们声明了两个function
,largest_i32
和largest_char
,它们的作用都是用来寻找list
中最大的那个元素,然后返回。
这俩函数唯一不同的地方就在于类型,元素的类型不同,但是由于强类型的问题,需要写两套代码,这就很难受。
这个时候我们就可以用泛型,我们来改下代码
这样是不是就少了很多重复代码
不过这里有个报错
不能用泛型来比较大小,这是必然的,因为连类型都不确定。
不过它有个help
这是一个trait
,如果这个T
标记了这个std::cmp::PartialOrd
,那么就可以比较(当然不是所有类型都可以,而这里char
和i32
都实现了这个trait
)。下一章就会说到trait
,所以这里我们还是跳过。
从这上面的例子可以发现,泛型可以让我们节省下很多重复的逻辑代码。
直接开看下例子
规范是定义在名字和{}
之间
但是构造体有一个特点,就是类型可以不同,所以往往一个T
并不能满足所有需求,比如
这样就有问题了,x
是i32
,而y
是f64
。
所以这个时候得改下我们的泛型定义
这样就没问题了
最典型的就是我们的Option
和Result
了。
就不多说了
来看下例子
开始有那么一点复杂了。。
这里有两个T
,一个是impl
后面的,一个是Point
后面的,而 -> &T
这个是impl
后面的T
,而Ponit
后面的T
实际上也是impl
后面那个T
,我们来改下impl
后面那个T
结果发现俩T
都不得劲了。
那到底啥意思呢?
我们再来看个例子
只有Point
的类型可以使用这个distance_from_origin
的method
。
这就很舒服了,不同类型可以有不同的method
,并且也不存在错用乱用的情况。当然,如果不确定类型就说明共用,所有类型都能用。
我们再来看个例子
这个例子就有点复杂了。。。这个mixup
方法实际上是将自己的x
和另一个实例的y
结合,生成一个新的实例,类型是Point
。
有四个泛型X1,Y1,X2,Y2
,其中Point
后面的只能用这个X1,Y1
,因为X2,Y2
都是mixup
这个方法的泛型。
也就是说这个同样是受限于作用域规则。
你应该会担心这玩意儿的性能问题(其实并没有),由于类型并不固定,所以会担心有runtime
的花费(不好意思,也没有)。
实际上并没有runtime
的花费,因为rust
在编译的时候使用泛型来执行代码的单态化(monomorphization
)使得runtime
之前就确定下来了。这句话我翻译的应该不是很对。。。我把原文贴出来
啥意思呢?看着好高端的样子,实际上只是用一种特殊的方式执行这段代码然后确定下来类型而已,简单地说就是在编译的时候做类似runtime
的操作确定下来类型(monomorphization是一个process
也就是进程)。
我们来看个例子
这是一个Option
的变体,它的值是一个泛型,在编译的时候发现了这段代码,monimorphization
就开始运行啦,然后它跑完全部代码之后发现使用Option
的枚举泛型有两个,一个是Option
,一个是Option
,这个时候它就会把Option
拓展为i32
和f64
的类型。这样你这代码的类型就固定下来了,也就是不再存在T
,而是i32
和f64
。
简单的说就像是语法糖,编译的时候会帮你编译成你手动去固定类型一样。如下面这段代码
看起来好像很6
。
其实各位如果看过一些框架的编译器源码(比如说vue
的sfc
编译器),你就不会觉得这操作666
了。
今天我们学了泛型,使用上和其他语言的泛型有点差别,但是不大。
最后如果觉得这篇文章对你有帮助的话,请务必点个赞,谢谢·~
发布于 2022-12-14 22:47・IP 属地广东