函数

函数定义

使用fn关键字定义函数:

fn main() {
	println(add(77, 33))
	println(sub(100, 50))
}

fn add(x int, y int) int {
	return x + y
}

fn sub(x int, y int) int {
	return x - y
}

V语言的函数定义(函数签名)基本跟go一样,去除各种视觉干扰的符号,简洁清晰,而函数关键字是跟rust一样的fn,更简洁。

函数的命名强制采用rust一样的小蛇式风格(lower snake case),即小写+下划线的风格,看着很舒服,而不是采用go的大小写来区分可访问性,这样就不会强制要大小写。

主函数

程序从主函数开始运行,主函数的签名中没有参数,也没有返回值,简单干净。

从命令行启动程序时,传递给程序的参数会统一存放在os.args这个字符串数组中,只读,全局可以访问。

主函数如果中途要退出,使用exit(code int)内置函数,并返回错误码给操作系统。

函数访问控制

函数默认模块内部访问,使用pub才可以被模块外部访问。

函数参数

函数的参数采用的是类型后置,相同类型的相邻参数不可以合并类型。

函数的参数默认是不可变的,如果在函数内部要改变传进来的参数,要在函数参数的定义和调用中都要加上mut

不确定个数参数也是支持的,不确定参数要放在参数的最后一个。

数组可以传递给不确定参数函数,不确定参数函数之间也可以传递参数。

不确定个数参数的类型也可以是指针类型:

函数返回值

函数的返回值可以是单返回值,也可以是多返回值。

因为有了这个特性,其他语言中有的元组类型Tuple,在V语言中就不太需要了。

返回值也可以返回指针类型:

跟go一样,可以使用下划线来忽略函数的某个返回值。

函数defer语句

在函数退出前执行defer代码块,一般用来在函数执行完毕后,释放资源的占用。一个函数可以有多个defer代码块,采用后定义先执行(后进先出LIFO)的原则。

同时,在defer代码块内有一些特殊的注意事项:

  • defer代码块内不可调用return

  • defer代码块内不可嵌套定义defer代码块

  • defer代码块内调用函数时,不可向上抛转错误,例如 a=test1() ?

执行结果:

可以使用$res()函数,在defer语句中取得函数的返回值:

函数类型

相同的函数签名表示同一类函数,可以用type定义为函数类型。

函数作为参数

函数作为返回值

闭包的参数传递

函数支持闭包,上级函数和闭包函数是两个单独的作用域,要将上级函数中的变量要传递给闭包函数,需要用[中括号]特别列出来,这样闭包函数内才可以正常使用上级函数的变量。

如果要传递的是变量的引用也可以,不过要确保变量在调用函数的时候始终可用,否则就会产生错误。

lambda表达式

支持简写的lambda表达式,类似于匿名函数,竖线中间是函数的参数,不用标识类型,后面带一个表达式作为函数的返回值:

函数递归

函数支持递归调用:

泛型函数

函数支持泛型,参考泛型章节

函数重载

V语言不支持函数重载,同一个函数名,在模块中只能有一个,即使是参数不一样。

以下代码无法通过编译:

函数参数默认值

函数的参数没有默认值这个特性。不过可以使用结构体参数来实现同样的效果。

函数命名参数

可以使用结构体作为参数,实现命名参数的效果,这个方式比语言上直接实现多了一个步骤,就是要先创建一个结构体参数。

函数使用结构体作为参数,参数的传递只根据参数名来匹配,跟参数的顺序无关。

这个语法在ui模块比较常用到,用来让函数的参数可读性更好。

结构体参数

使用结构体作为函数的参数,并且结构体使用了[params]注解,就可以实现参数的默认值和命名参数传参的效果。

结构体参数加了[params]注解,函数的参数可以什么都不传,直接使用结构体字段的默认值,如果结构体字段没有特别指定默认值,默认就是零值初始化。

内联函数

可以对函数添加[inline]注解,主要的用途是在调用C代码库的时候使用最多,内联函数跟C的内联函数概念一样,生成的也是C的内联函数。

详细参考:调用C代码库

匿名/内部函数

可以在函数内部定义匿名函数:

函数注解

@[deprecated]

模块发布给其他用户使用后,如果模块的某个函数想要声明作废或者被代替,可以使用作废注解。

@[inline]

inline注解的功能跟C函数的inline函数一样。

@[unsafe]

参考不安全代码章节

@[trusted]

参考不安全代码章节

@[live]

代码热更新功能,只对函数内的代码热更新生效,改变结构体是不行的。实际生产用处不大,开发时可以使用,像脚本语言那样,保存即生效,不用重新运行程序。v watch run . 也有类似效果,差别是watch实际上是自动监控文件变化,自动重新运行程序。

运行时也需要加上-live选项,才会有效果。

@[export]

一般来说V函数编译生成C代码时,C函数名会自动变为[模块名+双下划线+函数名],比如在main模块中的add函数会变为main__add。

使用export注解可以自定义V函数生成的C函数名称,一般用于C函数中调用V函数时,可直接使用export中自定义的函数名。

生成的C代码:

实际上的实现方式是除了生成默认的main__add函数外,又生成了一个export注解设置的C函数。

@[weak]

weak注解目前只支持函数函数和全局变量,weak注解告诉C编译器,该函数或全局变量是一个弱类型的,跟其他C代码一起编译时,在链接的时候,如果编译生成的C代码中存在一个同名的函数或全局变量(强类型的),则使用这个强类型的,链接器不会报重复定义的错误。目前看这个注解大部分的情况用在c2v的代码中,而且一般跟[export]注解一起使用,一般的V代码很少用到这种场景。

@[if xxx平台]

如果函数加上这个注解,只有在对应的平台上才编译生成这个函数,其他平台不会生成,函数的调用也会忽略。

内置函数

V在内置模块builtin中,内置了一些函数,可以全局使用,所有的内置函数可以在vlib/builtin目录中找到定义:

dump函数

跟C语言中的dump函数功能一样:把某个表达式的数据转储,并输出,dump函数主要用于调试,比println函数更为方便,清晰。

表达式被调用了几次,每次的结果如何,代码的位置,都会被清楚地打印出来。

输出:

最后更新于

这有帮助吗?