泛型

泛型,最简单的理解就是:类型作为参数,在结构体或函数中使用.

有了泛型,就可以编写出更为简洁,通用,抽象的代码.

V的泛型目前支持三种:泛型函数,泛型结构体,泛型方法.

泛型函数

在泛型函数中,所有普通类型能使用的场景,泛型也应该要能使用,

泛型跟普通类型一样,可以作为:

  • 函数参数的类型
  • 函数返回值的类型
  • 函数不确定个数参数的类型
  • 函数数组参数的类型
  • 数组的类型
  • 多维数组的类型
  • 固定大小数组的类型
  • 泛型支持类型系统中的:所有基本类型,数组,字典类型,信道chan的类型
  • 甚至泛型函数和泛型结构体也可以组合起来使用
  • 其他各种普通类型能用的场景…
  1. module main
  2. fn main() {}
  3. fn simple<T>(p T) T { // 泛型作为函数的参数,返回值
  4. return p
  5. }
  6. fn multi<T, U>(a T, b U) (T, U) { // 多个泛型
  7. return a, b
  8. }
  9. fn max<T>(brug string, a ...T) T { //泛型作为不确定参数的类型
  10. mut max := a[0]
  11. for item in a[1..] {
  12. if max < item {
  13. max = item
  14. }
  15. }
  16. return max
  17. }
  18. fn get_element<T>(arr [3]T) string { // 泛型作为固定大小数组的类型
  19. return '${arr[1]}'
  20. }
  21. fn f_array<T>(a []T) T { // 泛型作为数组的类型
  22. return a[0]
  23. }
  24. fn example2<T>(data [][]T) [][][]T { // 泛型作为多维数组的类型
  25. return [data]
  26. }
  27. fn generic_return_map<M>() map[string]M { // 泛型作为map的value类型
  28. return map{
  29. '': M{}
  30. }
  31. }
  32. fn generic_return_nested_map<M>() map[string]map[string]M { //泛型作为嵌套的map类型
  33. return map{
  34. '': map{
  35. '': M{}
  36. }
  37. }
  38. }
  39. fn opt<T>(v T) ?T { //泛型作为返回值,并结合错误处理
  40. if sizeof(T) > 1 {
  41. return v
  42. }
  43. return none
  44. }
  45. fn mk_chan<T>(f fn () T) chan T { // 泛型作为chan类型
  46. gench := chan T{cap: 1}
  47. return gench
  48. }
  49. struct Test<T> {
  50. v T
  51. }
  52. fn get_test<T>(v T) Test<T> { // 泛型函数和泛型结构体组合使用
  53. return Test{
  54. v: v
  55. }
  56. }

泛型函数的调用方式有标准方式简洁方式这两种.

简洁方式只有泛型在函数参数中有使用才可以,编译器会自动根据参数具体的类型,传递类型信息给函数,让调用泛型函数看起来像是调用普通函数那样.

  1. module main
  2. //泛型函数
  3. fn get<T>(typ T) T {
  4. return typ
  5. }
  6. //多个类型的泛型函数,参数,函数体,返回值都可以使用
  7. fn get_multi<T,U>(typ T,user U) (T,U) {
  8. println(typ)
  9. println(user)
  10. return typ,user
  11. }
  12. fn main() {
  13. a1 := get<string>('hello') //标准的泛型调用方式,带<T>
  14. a2 := get<int>(1)
  15. println(a1)
  16. println(a2)
  17. b1 := get('hello') //简短的泛型调用方式,不用带<T>
  18. b2 := get(1)
  19. println(b1)
  20. println(b2)
  21. x,y:=get_multi<int,f64>(2,2.2) //标准的泛型调用方式,带<T,U>
  22. println('x is $x,y is $y')
  23. c,d:=get_multi(3,3.3) //简短的泛型调用方式,不用带<T,U>
  24. println('c is $c,d is $d')
  25. }

泛型结构体

在泛型结构体中,所有普通类型能使用的场景,泛型也应该要能使用,

泛型跟普通类型一样,在结构体中可以作为:

  • 结构体字段的类型
  • 结构体的引用类型&
  • 结构体函数类型字段的参数或返回值
  • 泛型函数的嵌套使用
  • 泛型结构体的嵌套使用
  • 泛型结构体的泛型方法
  • 甚至泛型函数和泛型结构体也可以组合起来使用
  • 其他各种普通类型能用的场景…
  1. module main
  2. struct Info<T> { //泛型结构体
  3. data T //泛型作为字段的类型
  4. }
  5. struct Foo<A, B> { // 多个泛型
  6. mut:
  7. a A
  8. b B
  9. }
  10. struct MyStruct<T> {
  11. arr []T // 泛型作为结构体字段的数组
  12. arr2 [3]T //泛型作为结构体字段的固定大小数组
  13. m1 map[string]T //泛型作为结构体字段的map
  14. c chan T //泛型作为结构体字段的chan
  15. }
  16. struct Scope<T> {
  17. before fn () T // 泛型作为结构体函数类型字段的返回值
  18. specs []fn (T) T //// 泛型作为结构体函数类型字段的参数和返回值
  19. after fn (T) // 泛型作为结构体函数类型字段的参数
  20. }
  21. struct Item<T> {
  22. value T
  23. }
  24. fn (i Item<T>) unwrap() T {
  25. return i.value
  26. }
  27. fn process<T>(i Item<T>) { //泛型函数和泛型结构体组合使用,泛型结构体作为泛型函数的返回值
  28. n := i.unwrap()
  29. println(n)
  30. }

泛型方法

泛型方法基本跟泛型函数一样,唯一的差别是如果结构体也是泛型结构体,方法的接收者也都要带上泛型符号.

  1. module main
  2. struct Point {
  3. mut:
  4. x int = 1
  5. y int = 1
  6. }
  7. //结构体是普通结构体,方法是泛型方法
  8. fn (mut p Point) translate<T>(x T, y T) {
  9. p.x += x
  10. p.y += y
  11. }
  12. struct Abc<T> {
  13. value T
  14. }
  15. //结构体是泛型结构体,方法的接收者都要带上<T>
  16. fn (s Abc<T>) get_value() T { //泛型结构体的泛型方法
  17. return s.value
  18. }
  19. fn (s Abc<T>) normal_fn() { //泛型结构体的普通方法
  20. println('from normal_fn')
  21. }
  22. fn main() {
  23. mut pot := Point{}
  24. pot.translate<int>(1, 3)
  25. println(pot)
  26. s := Abc<string>{'hello'}
  27. println(s.get_value())
  28. s.normal_fn()
  29. }

泛型的实现方式

V的泛型实现方式,跟rust一样,在编译时,由编译器对泛型函数和泛型结构体进行分析,穷举,把泛型函数所有实际调用到的具体类型,转化成具体类型对应的普通函数和普通结构体.

这种实现方式的优点是性能好,没有运行时开销,缺点是如果实际调用到的类型很多,穷举后生成的普通函数和普通结构体也会很多,编译后的可执行文件会变大.

这种取舍在所难免,不过运行快最重要,可执行文件变大,只要不是太夸张,还是可以接受的.

V泛型代码:

  1. module main
  2. //泛型函数
  3. fn simple<T>(p T) T {
  4. return p
  5. }
  6. //泛型结构体
  7. struct Info<T> {
  8. data T
  9. }
  10. fn main() {
  11. simple<int>(1)
  12. simple<int>(1 + 0)
  13. simple<string>('g')
  14. simple<[]int>([1])
  15. simple<map[string]string>(map{'a': 'b'})
  16. info1:= Info<bool>{true}
  17. info2:= Info<int>{1}
  18. info3:= Info<f32>{1.1}
  19. println('$info1,$info2,$info3')
  20. }

生成的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;
  5. struct main__Info_T_bool {
  6. bool data;
  7. };
  8. struct main__Info_T_int {
  9. int data;
  10. };
  11. struct main__Info_T_f32 {
  12. f32 data;
  13. };
  14. VV_LOCAL_SYMBOL int main__simple_T_int(int p) {
  15. return p;
  16. }
  17. VV_LOCAL_SYMBOL string main__simple_T_string(string p) {
  18. return p;
  19. }
  20. VV_LOCAL_SYMBOL Array_int main__simple_T_Array_int(Array_int p) {
  21. return p;
  22. }
  23. VV_LOCAL_SYMBOL Map_string_string main__simple_T_Map_string_string(Map_string_string p) {
  24. return p;
  25. }
  26. VV_LOCAL_SYMBOL void main__main(void) {
  27. main__simple_T_int(1);
  28. main__simple_T_int(1 + 0);
  29. main__simple_T_string(_SLIT("g"));
  30. main__simple_T_Array_int(new_array_from_c_array(1, 1, sizeof(int), _MOV((int[1]){1})));
  31. 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"), })));
  32. main__Info_T_bool info1 = (main__Info_T_bool){.data = true,};
  33. main__Info_T_int info2 = (main__Info_T_int){.data = 1,};
  34. main__Info_T_f32 info3 = (main__Info_T_f32){.data = 1.1,};
  35. 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 }}})) );
  36. }

尚未支持的特性

目前V的泛型还没实现:

  • 对类型进行where限定,给类型增加条件限定
  • 泛型跟类型系统中的联合类型,接口类型之间的结合使用