函数

函数定义

使用fn关键字定义函数

1. fn main() {
2. println(add(77, 33))
3. println(sub(100, 50))
4. }

6. fn add(x int, y int) int {
7. return x + y
8. }

10. fn sub(x int, y int) int {
11. return x - y
12. }

V语言的函数定义(函数签名)基本跟go一样

去除各种视觉干扰的符号,简洁清晰,而函数关键字是跟rust一样的fn,更简洁

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

函数访问控制

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

1. module mymodule

3. fn private_fn() { // 模块内部可以访问,模块外部不可以访问
4. }

6. pub fn public_fn() { // 模块内部和外部都可以访问
7. }

函数参数

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

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

1. fn my_fn(mut arr []int) { // 1.参数定义也要是可变的
2. for i := 0; i < arr.len; i++ {
3. arr[i] *= 2
4. }
5. }

7. fn main() {
8. mut nums := [1, 2, 3] // 2.传进来的参数要是可变的
9. my_fn(mut nums) // 3.调用函数时,还需要在参数前加上mut,不然会报错
10. println(nums) // 返回[2, 4, 6],函数执行后,传进来的参数被改变了
11. }

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

1. fn my_fn(i int, s string, others ...string) {
2. println(i)
3. println(s)
4. println(others[0])
5. println(others[1])
6. println(others[2])
7. }

9. fn main() {
10. my_fn(1, 'abc', 'de', 'fg', 'hi')
11. }

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

1. module main

3. fn main() {
4. a := ['a', 'b', 'c']
5. println(variadic_fn_a(...a)) //数组解构赋值后,传递给不确定参数数组
6. }

8. fn variadic_fn_a(a ...string) string {
9. return variadic_fn_b(...a) //数组解构赋值后,传递给不确定参数数组
10. }

12. fn variadic_fn_b(a ...string) string {
13. a0 := a[0]
14. a1 := a[1]
15. a2 := a[2]
16. return '$a0$a1$a2'
17. }

函数返回值

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

1. fn bar() int { //单返回值
2. return 2
3. }

5. fn foo() (int, int) { //多返回值
6. return 2, 3
7. }

9. fn some_multiret_fn(a int, b int) (int, int) {
10. return a + 1, b + 1 //可以返回表达式
11. }

13. fn main() {
14. a, b := foo()
15. println(a) // 2
16. println(b) // 3
17. }

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

1. fn get_the_dao_way() voidptr { //返回通用指针
2. return voidptr(0)
3. }

5. fn multi_voidptr_ret() (voidptr, bool) { //返回通用指针
6. return voidptr(0), true
7. }

9. fn multi_&byte_ret() (&byte, bool) { //返回字节指针
10. return &byte(0), true
11. }

忽略返回值

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

1. fn main() {
2. a, _ := foo() // 忽略第二个返回值
3. _, _ := foo() // 也可以忽略全部的返回值
4. println(a) // 2
5. b := bar()
6. _ := bar()
7. println(b) // 2
8. }

10. fn bar() int {
11. return 2
12. }

14. fn foo() (int, int) {
15. return 2, 3
16. }

函数defer语句

在函数退出前执行defer代码块,一般用来在函数执行完毕后,释放资源的占用.

一个函数可以有多个defer代码块,采用后定义先执行(后进先出)的原则.

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

  • defer代码块内不可调用return
  • defer代码块内不可嵌套定义defer代码块
  • defer代码块内调用函数时,不可向上抛转错误,例如 a=test1() ?
1. fn main() {
2. println('main start')
3. // defer {defer_fn1()} //写成单行也可以
4. // defer {defer_fn2()}
5. defer {
6. defer_fn1()
7. }
8. defer {
9. defer_fn2()
10. }
11. println('main end')
12. }

14. fn defer_fn1() {
15. println('from defer_fn1')
16. }

18. fn defer_fn2() {
19. println('from defer_fn2')
20. }

执行结果:

1. main start
2. main end
3. from defer_fn2
4. from defer_fn1

函数类型

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

1. type Mid_fn = fn (int, string) int

函数作为参数

1. fn sqr(n int) int {
2. return n * n
3. }

5. fn run(value int, op fn (int) int) int {
6. return op(value)
7. }

9. fn main() {
10. println(run(5, sqr)) // "25" sql函数作为参数传递
11. }

函数作为返回值

1. module main

3. fn add(x int) int {
4. return x
5. }

7. fn sum() fn (int) int { // 函数类型作为返回值
8. return add
9. }

11. fn main() {
12. a := sum()
13. println(a(2))
14. }

函数递归

函数也是可以递归调用的

泛型函数

参考泛型章节

函数重载

V不会有函数重载

函数默认值

函数的参数目前还没有参数默认值这个特性

内联函数

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

1. [inline]
2. pub fn width() int {
3. return C.sapp_width()
4. }

详细参考:调用C代码库

匿名/内部函数

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

1. import sync

3. fn main() {
4. f1 := fn (a int) { // 定义函数类型变量
5. println('hello from f1')
6. }
7. f1(1)
8. f2 := fn (a int) { // 定义函数类型变量
9. println('hello from f2')
10. }
11. f2(1)
12. fn (a int) { // 匿名函数定义同时调用
13. println('hello from anon_fn')
14. }(1)
15. // 匿名函数结合go使用
16. mut wg := sync.new_waitgroup()
17. go fn (mut wg sync.WaitGroup) {
18. println('hello from go')
19. wg.done()
20. }(mut wg)
21. wg.wait()
22. }

函数注解

deprecated

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

1. [deprecated] //函数作废注解
2. pub fn ext(path string) string {
3. panic('Use `filepath.ext` instead of `os.ext`') //结合panic进行报错提示
4. }
5. [deprecated:'可以在这里写说明'] //带说明的作废注解,编译器提示函数作废的时候会显示出来
6. pub fn (p Point) position() (int, int) {
7. return p.x, p.y
8. }

10. [deprecated:'这是作废说明']
11. [deprecated_after: '2021-03-01']  //在某个日期后开始作废,一定要放在deprecated后,才会警告
12. pub fn fn1() (int, int) (int, int) {
13. return p.x, p.y
14. }
15. //函数被调用,就会出现如下警告:
16. //warning: function `fn1` has been deprecated since 2021-03-01; 这是作废说明

inline

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

1. [inline] //函数inline注解
2. pub fn sum(x y int) int {
3. return x+y
4. }

6. [inline] //方法inline注解
7. pub fn (p Point) position() (int, int) {
8. return p.x, p.y
9. }

unsafe

参考不安全代码章节

trusted

参考不安全代码章节

live

代码热更新功能,实际生产用处不大

开发时可以使用,像脚本语言那样,保存即生效,不用重启服务器,正式发布的时候还是要去除live注解,有些麻烦

1. module main

3. import time

5. [live]
6. fn print_message() {
7. println('更新时间:${time.now()}')
8. println('该函数范围内的代码修改后,不需要重新编译运行,保存即生效')
9. }

11. fn main() {
12. for {
13. print_message()
14. time.sleep(1*time.second)
15. }
16. }

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

1. v -live run  main.v

内置函数

V内置了一些函数,可以全局使用

dump函数

跟C语言中的dump函数功能一样,把某个表达式的数据转储,并输出

dump函数主要用于调试,比println函数更为方便,清晰

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

1. fn factorial(n u32) u32 {
2. if dump(n <= 1) {
3. return dump(1)
4. }
5. return dump(n * factorial(n - 1))
6. }

8. fn main() {
9. println(factorial(5))
10. }

输出:

1. [xxx/main.v:2] n <= 1: false
2. [xxx/main.v:2] n <= 1: false
3. [xxx/main.v:2] n <= 1: false
4. [xxx/main.v:2] n <= 1: false
5. [xxx/main.v:2] n <= 1: true
6. [xxx/main.v:3] 1: 1
7. [xxx/main.v:5] n * factorial(n - 1): 2
8. [xxx/main.v:5] n * factorial(n - 1): 6
9. [xxx/main.v:5] n * factorial(n - 1): 24
10. [xxx/main.v:5] n * factorial(n - 1): 120