返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

5.5 结构

发布于 2024-10-12 19:15:48 字数 3997 浏览 0 评论 0 收藏 0

结构体将多个字段序列打包成一个复合类型。

  • 直属字段名必须唯一。
  • 支持自身指针类型成员。
  • 可用 _ 忽略字段名。
  • 支持匿名结构和空结构。
  • 支持匿名嵌入其他类型。
  • 支持为字段添加标签。
  • 仅所有字段全部支持,才可做相等操作。
  • 可用指针选择字段,但不支持多级指针。
  • 字段的名称、类型、标签及排列顺序属类型组成部分。
  • 除对齐外,编译器不会优化或调整布局。

顺序初始化,必须包含全部字段值。而命名初始化,不用全部字段,也无关顺序。
建议使用命名方式,即便后续新增字段或调整排列顺序也不受影响。

type node struct {
	id    int
	value string
	next  *node        // 自身指针类型。
}

func main() {

    // 顺序提供所有字段值,或全部忽略。
    
	// n := node{ 1, "a" }
    //      ~~~~~~~~~~~~~~ too few values in struct literal
    
    // 按字段名初始化。
    
	n := node{
		id   : 2,
		value: "abc",  // 注意结尾逗号 !!!
	}
}
func main() {
    
    // 匿名结构
	user := struct {
		id   int
		name string
	}{
		id  : 1,
		name: "user1",
	}

	fmt.Printf("%+v\n", user)
    
    // --------------------

	var color struct {
		r int
		g int
		b int
	}

	color.r = 1
	color.g = 2
	color.b = 3

	fmt.Printf("%+v\n", color)
}

// {id:1 name:user1}
// {r:1 g:2 b:3}
func main() {

	type file struct {
		name string
		attr struct {
			owner int
			perm  int
		}
	}

	f := file{
		name: "test.dat",

		// 因缺少类型标签,无法直接初始化。

		// attr: {          
		//    owner: 1,
		//    perm:  0755,
		// },
		// ~~~~~~~~~ missing type in composite literal
	}

	f.attr.owner = 1
	f.attr.perm = 0755

	fmt.Println(f)
}

相等操作限制:

  • 全部字段支持。
  • 内存布局相同,但类型不同,不能比较。
  • 布局相同(含字段名、标签等)的匿名结构视为同一类型。
func main() {
	type data struct {
		x int
		y map[string]int
	}

	d1 := data{ x: 100 }
	d2 := data{ x: 100 }

	// _  = d1 == d2
	//      ~~~~~~~~ invalid: struct containing map cannot be compared
}
func main() {
    
    // 类型不同!
    
	type data1 struct {
		x int
	}

	type data2 struct {
		x int
	}

	d1 := data1{ x: 100 }
	d2 := data2{ x: 100 }

	// _  = d1 == d2
    //      ~~~~~~~~ invalid: mismatched types data1 and data2
}
func main() {
    
    // 匿名结构类型相同的前提是
    // 字段名、字段类型、标签和排列顺序都相同。
    
	d1 := struct {
		x int
		s string
	}{ 100, "abc" }

	var d2 struct {
		x int
		s string
	}

	d2.x = 100
	d2.s = "abc"

	println(d1 == d2) // true
}

不能以多级指针访问字段成员。

func main() {
	type user struct {
		name string
		age  int
	}

    // 直接返回指针。
	p := &user{
		name: "u1",
		age:  20,
	}

	p.name = "u2"
	p.age++

    // 二级指针。
	p2 := &p
	
	// p2.age++
	// ~~~~~~ p2.age undefined (type **user has no field or method age)
}

空结构

空结构( struct{} )是指没有字段的结构类型,常用于值可被忽略的场合。
无论是其自身,还是作为元素类型,其长度都为零,但这不影响作为实体存在。

func main() {
	var a struct{}
	var b [1000]struct{}

	s := b[:]

	println(unsafe.Sizeof(a), unsafe.Sizeof(b))  // 0, 0
	println(len(s), cap(s))                      // 1000, 1000
}
func main() {
    
    // 结束通知,值无关紧要。
	done := make(chan struct{})

	go func(){
		time.Sleep(time.Second)
		close(done)
	}()

	<- done
}
func main() {
	nul := struct{}{}
	users := make(map[int]struct{})

	for i := 0; i < 100; i++ {
		users[i] = nul
	}

	// 利用字典随机选人,值不需要。
	for k, _ := range users {
		println(k)
		break
	}
}

标签

标签(tag)不是注释,而是对字段进行描述的元数据。

  • 不是数据成员,却是类型的组成部分。
  • 内存布局相同,允许显式类型转换。
func main() {

	type user struct {
		id int `id`
	}

	type user2 struct {
		id int `uid`      // !!!!
	}

	u1 := user{1}
	u2 := user2{2}

	// 类型不同。

	// _ = u1 == u2
	//     ~~~~~~~~ mismatched types user and user2

	// 内存布局相同,支持转换。
	u1 = user(u2)
	fmt.Println(u1)
}

运行期,可用反射获取标签信息。常被用作格式校验,数据库关系映射等。

package main

import (
	"fmt"
	"reflect"
)

func main() {

	type User struct {
		id   int     `field:"uid"  type:"integer"`
		name string  `field:"name" type:"text"`
	}

	t := reflect.TypeOf(User{})

    for i := 0; i < t.NumField(); i++ {
        f := t.Field(i)
        fmt.Println(f.Name, f.Tag.Get("field"), f.Tag.Get("type"))
    }	
}

//   id: uid  integer
// name: name text

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文