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

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

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

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

    • 函数参数的类型
    • 函数返回值的类型
    • 函数不确定个数参数的类型
    • 函数数组参数的类型
    • 数组的类型
    • 多维数组的类型
    • 固定大小数组的类型
    • 泛型支持类型系统中的:所有基本类型,数组,字典类型,信道chan的类型
    • 甚至泛型函数和泛型结构体也可以组合起来使用
    • 其他各种普通类型能用的场景…

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

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

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

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

    • 结构体字段的类型
    • 结构体的引用类型&
    • 结构体函数类型字段的参数或返回值
    • 泛型函数的嵌套使用
    • 泛型结构体的嵌套使用
    • 泛型结构体的泛型方法
    • 甚至泛型函数和泛型结构体也可以组合起来使用
    • 其他各种普通类型能用的场景…

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

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

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

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

    V泛型代码:

    生成的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. bool data;
    6. };
    7. struct main__Info_T_int {
    8. int data;
    9. };
    10. struct main__Info_T_f32 {
    11. f32 data;
    12. };
    13. VV_LOCAL_SYMBOL int main__simple_T_int(int p) {
    14. return p;
    15. }
    16. VV_LOCAL_SYMBOL string main__simple_T_string(string p) {
    17. return p;
    18. }
    19. VV_LOCAL_SYMBOL Array_int main__simple_T_Array_int(Array_int p) {
    20. return p;
    21. }
    22. VV_LOCAL_SYMBOL Map_string_string main__simple_T_Map_string_string(Map_string_string p) {
    23. return p;
    24. }
    25. VV_LOCAL_SYMBOL void main__main(void) {
    26. main__simple_T_int(1);
    27. main__simple_T_int(1 + 0);
    28. main__simple_T_string(_SLIT("g"));
    29. main__simple_T_Array_int(new_array_from_c_array(1, 1, sizeof(int), _MOV((int[1]){1})));
    30. 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"), })));
    31. main__Info_T_bool info1 = (main__Info_T_bool){.data = true,};
    32. main__Info_T_int info2 = (main__Info_T_int){.data = 1,};
    33. main__Info_T_f32 info3 = (main__Info_T_f32){.data = 1.1,};
    34. 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 }}})) );
    • 对类型进行where限定,给类型增加条件限定
    • 泛型跟类型系统中的联合类型,接口类型之间的结合使用