结构体

结构体定义

结构体命名必须是大写字母开头,如果是小写字母开头会编译报错

  1. struct Point {
  2. x int
  3. y int
  4. }
  5. fn main() {
  6. p := Point{
  7. x: 10
  8. y: 20
  9. }
  10. println(p.x) // 结构体字段通过点号来访问
  11. }

结构体被分配到内存的栈中,引用类型

取结构体地址:&

  1. p2 := &Point{10, 10}
  2. println(p2.x)

空结构体:

  1. struct Empty {}
  2. fn main() {
  3. println(sizeof(Empty)) //空结构体占用内存的大小为0
  4. }

结构体占用内存:

sizeof( )可以返回结构体占用内存字节大小

字段

结构体字段默认也是不可变,使用mut为可变

结构体字段的可变性和访问控制,参考访问控制章节

如果结构体字段名需要是关键字,可以通过使用@作为前缀也可以编译通过

这一点在跟C库集成时,比较常用,一些C库的struct的字段有些刚好是V的关键字,可以使用@前缀,编译成C代码时@前缀会被去掉,刚好就是C的struct中的字段名

  1. struct Foo {
  2. @type string
  3. }
  4. fn main() {
  5. foo := Foo{ @type: 'test' }
  6. println(foo.@type)
  7. }

结构体字段支持默认值

  1. struct Foo {
  2. a int
  3. b int = 7 //默认值7
  4. }
  5. fn main() {
  6. foo := Foo{
  7. a: 1
  8. }
  9. println(foo.a) //输出1
  10. println(foo.b) //输出默认值7
  11. foo2 := Foo{
  12. a: 1
  13. b: 2
  14. }
  15. println(foo2.b) //输出2
  16. }

结构体字段的类型可以是任何类型,甚至可以是函数类型

  1. module main
  2. struct Abc {
  3. f1 int
  4. f2 int
  5. f3 fn (int, int) int //结构体字段类型为函数类型
  6. }
  7. fn add(x int, y int) int {
  8. return x + y
  9. }
  10. fn main() {
  11. a1 := Abc{
  12. f1: 123
  13. f3: fn (x int, y int) int {
  14. return x + y
  15. }
  16. }
  17. a2 := Abc{
  18. f1: 123
  19. f2: 789
  20. f3: add
  21. }
  22. println(a1)
  23. println(a2)
  24. }

结构体字段初始化时必录

  1. struct Point {
  2. x int
  3. y int
  4. z int [required] //字段注解required表示必须初始化赋值
  5. }
  6. fn main() {
  7. a := Point{
  8. // x: 1 //不会报错,x不是必须初始化赋值
  9. y: 3
  10. z: 5
  11. }
  12. b := Point{
  13. x: 2
  14. y: 4
  15. // z: 6 //报错: field `Point.z` must be initialized
  16. }
  17. println(a)
  18. println(b)
  19. }

结构体变量可以基于另一个变量创建,同时合并新的字段值

  1. module main
  2. struct City {
  3. name string
  4. population int
  5. }
  6. struct Country {
  7. name string
  8. capital City
  9. }
  10. fn main() {
  11. c := Country{
  12. name: 'test'
  13. capital: City{
  14. name: 'city'
  15. }
  16. }
  17. c2 := Country{
  18. ...c // 在c的基础上创建c2变量
  19. capital: City{
  20. name: 'city2'
  21. population: 200
  22. }
  23. }
  24. println(c2)
  25. }

短字面量创建结构体变量

当函数的参数是结构体变量时,这个语法可以简化结构体变量的创建,这个语法在ui模块比较常用到,用来简化函数参数

  1. module main
  2. struct User {
  3. name string
  4. age int
  5. }
  6. fn add(u User) {
  7. println(u)
  8. }
  9. fn main(){
  10. add(User{name:'jack',age:22}) //标准方式
  11. add({name:'tom',age:23}) //简短方式,省略类型
  12. add(name:'tt',age:33) //更简短的方式,省略类型和大括号,这个用法感觉分辨不出来参数是结构体,推荐使用方式二,兼具简短和清晰性
  13. }

访问控制

结构体默认是模块级别的,使用pub关键字定义公共级别

  1. pub struct Point { //公共级别,可以被别的模块使用
  2. x int
  3. y int
  4. }

组合

跟go一样,V的结构体没有继承,但是有组合,可以用组合来实现继承一样的效果,甚至可以多个组合

  1. module main
  2. struct Widget {
  3. mut:
  4. x int
  5. y int
  6. }
  7. pub fn (mut w Widget) move(x_step int, y_step int) {
  8. w.x += x_step
  9. w.y += y_step
  10. }
  11. struct Widget2 {
  12. mut:
  13. z int
  14. }
  15. pub fn (mut w Widget2) move_z(z_step int) {
  16. w.z += z_step
  17. }
  18. struct Button {
  19. Widget //组合
  20. Widget2 //多个组合
  21. title string
  22. }
  23. fn main() {
  24. mut button := Button{
  25. title: 'Click me'
  26. }
  27. button.x = 3 // Button的x字段来自Widget
  28. button.z = 4 // Button的z字段来自Widget2
  29. println('x:$button.x,y:$button.y,z:$button.z')
  30. button.move(3, 4) // Button的move方法来自Widget
  31. println('x:$button.x,y:$button.y,z:$button.z')
  32. button.move_z(5) // Button的move_z方法来自Widget2
  33. println('x:$button.x,y:$button.y,z:$button.z')
  34. }

泛型结构体

参考泛型章节

结构体注解

结构体像函数那样支持注解,可以针对结构体,结构体字段,结构体方法进行注解

[typedef]

  1. [typedef]
  2. struct Point {
  3. }

typedef注解目前主要用在集成C代码库,详细参考:

[heap]

heap表示该结构体只能在内存的堆上创建,只能以引用的形式被使用

  1. [heap]
  2. struct Window { //只能通过引用的形式(&Window)来使用这个结构体
  3. }

可以参考标准库sync中WaitGroup的实际使用

vlib/sync/waitgroup.v

  1. [heap]
  2. struct WaitGroup {
  3. mut:
  4. task_count int
  5. task_count_mutex &Mutex = &Mutex(0)
  6. wait_blocker &Waiter = &Waiter(0)
  7. }
  8. pub fn new_waitgroup() &WaitGroup { //不能被复制,只能以引用的方式被使用
  9. return &WaitGroup{
  10. task_count_mutex: new_mutex()
  11. wait_blocker: new_waiter()
  12. }
  13. }

[noinit]

使用[noinit]标志后,结构体只能在本模块内使用Foo{}来创建变量,在其他模块中被禁止使用Foo{}来初始化变量

  1. module mymodule
  2. [noinit]
  3. pub struct Result {
  4. }
  1. module main
  2. import mymodule
  3. fn main() {
  4. res := mymodule.Result{}
  5. println(res)
  6. }

结构体字段注解

  1. 用于内置json解析支持

    详细参考:json章节

  2. 结构体字段必须初始化赋值注解

  1. struct Point {
  2. x int
  3. y int
  4. z int [required] //字段注解required表示必须初始化赋值
  5. }
  6. fn main() {
  7. a := Point{
  8. // x: 1 //不会报错,x不是必须初始化赋值
  9. y: 3
  10. z: 5
  11. }
  12. b := Point{
  13. x: 2
  14. y: 4
  15. // z: 6 //报错: field `Point.z` must be initialized
  16. }
  17. println(a)
  18. println(b)
  19. }

结构体方法注解

跟函数注解一样,也可以对结构体方法进行注解

  1. [inline] //方法注解
  2. pub fn (p Point) position() (int, int) {
  3. return p.x, p.y
  4. }

结构体反射

可以通过编译时反射,实现动态获取结构体的所有字段,素有方法,并且可以动态设置字段值

具体可以参考编译时反射章节

结构体字段内存位置偏移

__offsetof函数实现C的offsetof函数那样,返回结构体中某个字段的内存偏移量

  1. module main
  2. struct User {
  3. name [50]byte
  4. age int
  5. desc string
  6. }
  7. fn main() {
  8. offset_name:=__offsetof(User,name)
  9. offset_age:=__offsetof(User,age)
  10. offset_desc:=__offsetof(User,desc)
  11. println(offset_name)
  12. println(offset_age)
  13. println(offset_desc)
  14. }