泛型
泛型,最简单的理解就是:类型作为参数,在结构体或函数中使用.
有了泛型,就可以编写出更为简洁,通用,抽象的代码.
V的泛型目前支持三种:泛型函数,泛型结构体,泛型方法.
泛型函数
在泛型函数中,所有普通类型能使用的场景,泛型也应该要能使用,
泛型跟普通类型一样,可以作为:
- 函数参数的类型
- 函数返回值的类型
- 函数不确定个数参数的类型
- 函数数组参数的类型
- 数组的类型
- 多维数组的类型
- 固定大小数组的类型
- 泛型支持类型系统中的:所有基本类型,数组,字典类型,信道chan的类型
- 甚至泛型函数和泛型结构体也可以组合起来使用
- 其他各种普通类型能用的场景…
1. module main
3. fn main() {}
5. fn simple<T>(p T) T { // 泛型作为函数的参数,返回值
6. return p
7. }
9. fn multi<T, U>(a T, b U) (T, U) { // 多个泛型
10. return a, b
11. }
13. fn max<T>(brug string, a ...T) T { //泛型作为不确定参数的类型
14. mut max := a[0]
15. for item in a[1..] {
16. if max < item {
17. max = item
18. }
19. }
20. return max
21. }
23. fn get_element<T>(arr [3]T) string { // 泛型作为固定大小数组的类型
24. return '${arr[1]}'
25. }
27. fn f_array<T>(a []T) T { // 泛型作为数组的类型
28. return a[0]
29. }
31. fn example2<T>(data [][]T) [][][]T { // 泛型作为多维数组的类型
32. return [data]
33. }
35. fn generic_return_map<M>() map[string]M { // 泛型作为map的value类型
36. return map{
37. '': M{}
38. }
39. }
41. fn generic_return_nested_map<M>() map[string]map[string]M { //泛型作为嵌套的map类型
42. return map{
43. '': map{
44. '': M{}
45. }
46. }
47. }
49. fn opt<T>(v T) ?T { //泛型作为返回值,并结合错误处理
50. if sizeof(T) > 1 {
51. return v
52. }
53. return none
54. }
56. fn mk_chan<T>(f fn () T) chan T { // 泛型作为chan类型
57. gench := chan T{cap: 1}
58. return gench
59. }
61. struct Test<T> {
62. v T
63. }
65. fn get_test<T>(v T) Test<T> { // 泛型函数和泛型结构体组合使用
66. return Test{
67. v: v
68. }
69. }
泛型函数的调用方式有标准方式和简洁方式这两种.
简洁方式只有泛型在函数参数中有使用才可以,编译器会自动根据参数具体的类型,传递类型信息给函数,让调用泛型函数看起来像是调用普通函数那样.
1. module main
3. //泛型函数
4. fn get<T>(typ T) T {
5. return typ
6. }
8. //多个类型的泛型函数,参数,函数体,返回值都可以使用
9. fn get_multi<T,U>(typ T,user U) (T,U) {
10. println(typ)
11. println(user)
12. return typ,user
13. }
15. fn main() {
16. a1 := get<string>('hello') //标准的泛型调用方式,带<T>
17. a2 := get<int>(1)
18. println(a1)
19. println(a2)
20. b1 := get('hello') //简短的泛型调用方式,不用带<T>
21. b2 := get(1)
22. println(b1)
23. println(b2)
25. x,y:=get_multi<int,f64>(2,2.2) //标准的泛型调用方式,带<T,U>
26. println('x is $x,y is $y')
27. c,d:=get_multi(3,3.3) //简短的泛型调用方式,不用带<T,U>
28. println('c is $c,d is $d')
29. }
泛型结构体
在泛型结构体中,所有普通类型能使用的场景,泛型也应该要能使用,
泛型跟普通类型一样,在结构体中可以作为:
- 结构体字段的类型
- 结构体的引用类型&
- 结构体函数类型字段的参数或返回值
- 泛型函数的嵌套使用
- 泛型结构体的嵌套使用
- 泛型结构体的泛型方法
- 甚至泛型函数和泛型结构体也可以组合起来使用
- 其他各种普通类型能用的场景…
1. module main
3. struct Info<T> { //泛型结构体
4. data T //泛型作为字段的类型
5. }
7. struct Foo<A, B> { // 多个泛型
8. mut:
9. a A
10. b B
11. }
13. struct MyStruct<T> {
14. arr []T // 泛型作为结构体字段的数组
15. arr2 [3]T //泛型作为结构体字段的固定大小数组
16. m1 map[string]T //泛型作为结构体字段的map
17. c chan T //泛型作为结构体字段的chan
18. }
20. struct Scope<T> {
21. before fn () T // 泛型作为结构体函数类型字段的返回值
22. specs []fn (T) T //// 泛型作为结构体函数类型字段的参数和返回值
23. after fn (T) // 泛型作为结构体函数类型字段的参数
24. }
27. struct Item<T> {
28. value T
29. }
31. fn (i Item<T>) unwrap() T {
32. return i.value
33. }
35. fn process<T>(i Item<T>) { //泛型函数和泛型结构体组合使用,泛型结构体作为泛型函数的返回值
36. n := i.unwrap()
37. println(n)
38. }
泛型方法
泛型方法基本跟泛型函数一样,唯一的差别是如果结构体也是泛型结构体,方法的接收者也都要带上泛型符号.
1. module main
3. struct Point {
4. mut:
5. x int = 1
6. y int = 1
7. }
9. //结构体是普通结构体,方法是泛型方法
10. fn (mut p Point) translate<T>(x T, y T) {
11. p.x += x
12. p.y += y
13. }
15. struct Abc<T> {
16. value T
17. }
19. //结构体是泛型结构体,方法的接收者都要带上<T>
20. fn (s Abc<T>) get_value() T { //泛型结构体的泛型方法
21. return s.value
22. }
24. fn (s Abc<T>) normal_fn() { //泛型结构体的普通方法
25. println('from normal_fn')
26. }
28. fn main() {
29. mut pot := Point{}
30. pot.translate<int>(1, 3)
31. println(pot)
33. s := Abc<string>{'hello'}
34. println(s.get_value())
35. s.normal_fn()
36. }
泛型的实现方式
V的泛型实现方式,跟rust一样,在编译时,由编译器对泛型函数和泛型结构体进行分析,穷举,把泛型函数所有实际调用到的具体类型,转化成具体类型对应的普通函数和普通结构体.
这种实现方式的优点是性能好,没有运行时开销,缺点是如果实际调用到的类型很多,穷举后生成的普通函数和普通结构体也会很多,编译后的可执行文件会变大.
这种取舍在所难免,不过运行快最重要,可执行文件变大,只要不是太夸张,还是可以接受的.
V泛型代码:
1. module main
3. //泛型函数
4. fn simple<T>(p T) T {
5. return p
6. }
8. //泛型结构体
9. struct Info<T> {
10. data T
11. }
13. fn main() {
14. simple<int>(1)
15. simple<int>(1 + 0)
16. simple<string>('g')
17. simple<[]int>([1])
18. simple<map[string]string>(map{'a': 'b'})
20. info1:= Info<bool>{true}
21. info2:= Info<int>{1}
22. info3:= Info<f32>{1.1}
23. println('$info1,$info2,$info3')
24. }
生成的C代码:
1. // V typedefs:
2. typedef struct main__Info_T_bool main__Info_T_bool;
3. typedef struct main__Info_T_int main__Info_T_int;
4. typedef struct main__Info_T_f32 main__Info_T_f32;
6. struct main__Info_T_bool {
7. bool data;
8. };
10. struct main__Info_T_int {
11. int data;
12. };
14. struct main__Info_T_f32 {
15. f32 data;
16. };
18. VV_LOCAL_SYMBOL int main__simple_T_int(int p) {
19. return p;
20. }
21. VV_LOCAL_SYMBOL string main__simple_T_string(string p) {
22. return p;
23. }
24. VV_LOCAL_SYMBOL Array_int main__simple_T_Array_int(Array_int p) {
25. return p;
26. }
27. VV_LOCAL_SYMBOL Map_string_string main__simple_T_Map_string_string(Map_string_string p) {
28. return p;
29. }
31. VV_LOCAL_SYMBOL void main__main(void) {
32. main__simple_T_int(1);
33. main__simple_T_int(1 + 0);
34. main__simple_T_string(_SLIT("g"));
35. main__simple_T_Array_int(new_array_from_c_array(1, 1, sizeof(int), _MOV((int[1]){1})));
36. main__simple_T_Map_string_string(new_map_init(&map_hash_string, &map_eq_string, &map_clone_string, &map_free_string, 1, sizeof(string), sizeof(string), _MOV((string[1]){_SLIT("a"), }), _MOV((string[1]){_SLIT("b"), })));
37. main__Info_T_bool info1 = (main__Info_T_bool){.data = true,};
38. main__Info_T_int info2 = (main__Info_T_int){.data = 1,};
39. main__Info_T_f32 info3 = (main__Info_T_f32){.data = 1.1,};
40. println( str_intp(4, _MOV((StrIntpData[]){{_SLIT0, 0xfe10, {.d_s = main__Info_T_bool_str(info1)}}, {_SLIT(","), 0xfe10, {.d_s = main__Info_T_int_str(info2)}}, {_SLIT(","), 0xfe10, {.d_s = main__Info_T_f32_str(info3)}}, {_SLIT0, 0, { .d_c = 0 }}})) );
41. }
尚未支持的特性
目前V的泛型还没实现:
- 对类型进行where限定,给类型增加条件限定
- 泛型跟类型系统中的联合类型,接口类型之间的结合使用
