一文教你如何快速学会Go的struct数据类型
作者:7small7 发布时间:2024-02-14 22:58:44
什么是结构体
结构是表示字段集合的用户定义类型。它可以用于将数据分组为单个单元而不是将每个数据作为单独的值的地方。
例如,员工有firstName、lastName和age。将这三个属性分组到一个名为Employee
。
type Employee struct {
firstName string
lastName string
age int
}
上面的代码段声明了一个结构类型Employee,其中包含字段firstName、lastName和age。上面的Employee结构称为命名结构,因为它创建了一个名为Employme的新数据类型,可以使用该数据类型创建Employ结构。
通过在一行中声明属于同一类型的字段,然后在类型名称后面加上该字段,也可以使该结构更加紧凑。在上面的struct中,firstName和lastName属于同一类型字符串,因此该struct可以重写为:
type Employee struct {
firstName, lastName string
age int
}
尽管上面的语法节省了几行代码,但它并没有使字段声明显式。请避免使用上述语法。
创建结构体
让我们使用以下简单程序声明一个命名的structEmployee。
package main
import (
"fmt"
)
type Employee struct {
firstName string
lastName string
age int
salary int
}
func main() {
//creating struct specifying field names
emp1 := Employee{
firstName: "Sam",
age: 25,
salary: 500,
lastName: "Anderson",
}
//creating struct without specifying field names
emp2 := Employee{"Thomas", "Paul", 29, 800}
fmt.Println("Employee 1", emp1)
fmt.Println("Employee 2", emp2)
}
在上述程序的第7行中,我们创建了一个命名的结构类型Employee。在上述程序的第17行中,emp1结构是通过为每个字段名指定值来定义的。声明结构类型时,字段的顺序不必与字段名的顺序相同。在这种情况下。我们已更改lastName的位置并将其移到末尾。这将不会有任何问题。
在上述程序的第25行中,通过省略字段名来定义emp2。在这种情况下,必须保持字段的顺序与结构声明中指定的顺序相同。请避免使用此语法,因为它会使您难以确定哪个字段的值。我们在此处指定此格式只是为了理解这也是一个有效语法:)
以上程序打印为:
Employee 1 {Sam Anderson 25 500}
Employee 2 {Thomas Paul 29 800}
创建匿名结构体
可以在不创建新数据类型的情况下声明结构。这些类型的结构称为匿名结构。
package main
import (
"fmt"
)
func main() {
emp3 := struct {
firstName string
lastName string
age int
salary int
}{
firstName: "Andreah",
lastName: "Nikola",
age: 31,
salary: 5000,
}
fmt.Println("Employee 3", emp3)
}
在上述程序的第8行中,定义了一个匿名结构变量emp3。正如我们已经提到的,这个结构称为anonymous,因为它只创建一个新的结构变量emp3,而没有定义任何新的结构类型,如命名结构。
上述代码打印的结果为:
Employee 3 {Andreah Nikola 31 5000}
访问结构体字段
运算符.
用于访问结构的各个字段。
package main
import (
"fmt"
)
type Employee struct {
firstName string
lastName string
age int
salary int
}
func main() {
emp6 := Employee{
firstName: "Sam",
lastName: "Anderson",
age: 55,
salary: 6000,
}
fmt.Println("First Name:", emp6.firstName)
fmt.Println("Last Name:", emp6.lastName)
fmt.Println("Age:", emp6.age)
fmt.Printf("Salary: $%d\n", emp6.salary)
emp6.salary = 6500
fmt.Printf("New Salary: $%d", emp6.salary)
}
上面程序中的emp6.firstName访问emp6结构的firstName字段。在第25行中,我们修改了员工的工资。此程序打印。
First Name: Sam
Last Name: Anderson
Age: 55
Salary: $6000
New Salary: $6500
结构体零值
当定义了一个结构并且没有用任何值显式初始化它时,默认情况下会为该结构的字段分配零值。
package main
import (
"fmt"
)
type Employee struct {
firstName string
lastName string
age int
salary int
}
func main() {
var emp4 Employee //zero valued struct
fmt.Println("First Name:", emp4.firstName)
fmt.Println("Last Name:", emp4.lastName)
fmt.Println("Age:", emp4.age)
fmt.Println("Salary:", emp4.salary)
}
上面的程序定义了emp4,但没有用任何值初始化。因此,firstName和lastName被指定为字符串的零值,字符串为空字符串“”,age和salary被指定为零值int,即0。此程序打印
First Name:
Last Name:
Age: 0
Salary: 0
也可以为某些字段指定值,而忽略其余字段。在这种情况下,被忽略的字段被赋值为零。
package main
import (
"fmt"
)
type Employee struct {
firstName string
lastName string
age int
salary int
}
func main() {
emp5 := Employee{
firstName: "John",
lastName: "Paul",
}
fmt.Println("First Name:", emp5.firstName)
fmt.Println("Last Name:", emp5.lastName)
fmt.Println("Age:", emp5.age)
fmt.Println("Salary:", emp5.salary)
}
在上面的程序中。第16号和第17号,firstName和lastName被初始化,而年龄和薪水没有初始化。因此,年龄和工资被指定为零值。此程序输出:
First Name: John
Last Name: Paul
Age: 0
Salary: 0
结构体指针
也可以创建指向结构的指针。
package main
import (
"fmt"
)
type Employee struct {
firstName string
lastName string
age int
salary int
}
func main() {
emp8 := &Employee{
firstName: "Sam",
lastName: "Anderson",
age: 55,
salary: 6000,
}
fmt.Println("First Name:", (*emp8).firstName)
fmt.Println("Age:", (*emp8).age)
}
上面程序中的emp8是指向Employee结构的指针。(*emp8)。firstName是访问emp8结构的firstName字段的语法。此程序打印:
First Name: Sam
Age: 55
Go语言为我们提供了使用emp8.firstName而不是显式取消引用(*emp8)的选项。firstName以访问firstName字段。
package main
import (
"fmt"
)
type Employee struct {
firstName string
lastName string
age int
salary int
}
func main() {
emp8 := &Employee{
firstName: "Sam",
lastName: "Anderson",
age: 55,
salary: 6000,
}
fmt.Println("First Name:", emp8.firstName)
fmt.Println("Age:", emp8.age)
}
我们已经使用emp8.firstName访问上述程序中的firstName字段,该程序还输出:
First Name: Sam
Age: 55
匿名字段
可以使用只包含类型而不包含字段名的字段创建结构。这类字段称为匿名字段。下面的代码段创建了一个struct Person,它有两个匿名字段string和int:
type Person struct {
string
int
}
即使匿名字段没有显式名称,默认情况下,匿名字段的名称是其类型的名称。例如,在上面的Person结构中,虽然字段是匿名的,但默认情况下它们采用字段类型的名称。所以Person结构有两个字段,分别是名称字符串和int。
package main
import (
"fmt"
)
type Person struct {
string
int
}
func main() {
p1 := Person{
string: "naveen",
int: 50,
}
fmt.Println(p1.string)
fmt.Println(p1.int)
}
在上述程序的第17行和第18行中,我们访问Person结构的匿名字段,使用它们的类型作为字段名,分别是string和int。上述程序的输出为:
naveen
50
结构体嵌套
结构可能包含字段,而字段又是结构。这些类型的结构称为嵌套结构。
package main
import (
"fmt"
)
type Address struct {
city string
state string
}
type Person struct {
name string
age int
address Address
}
func main() {
p := Person{
name: "Naveen",
age: 50,
address: Address{
city: "Chicago",
state: "Illinois",
},
}
fmt.Println("Name:", p.name)
fmt.Println("Age:", p.age)
fmt.Println("City:", p.address.city)
fmt.Println("State:", p.address.state)
}
上述程序中的Person结构具有字段地址,而字段地址又是一个结构。此程序打印:
Name: Naveen
Age: 50
City: Chicago
State: Illinois
字段升级
属于结构中匿名结构字段的字段称为提升字段,因为可以像访问包含匿名结构字段结构一样访问它们。我可以理解这个定义相当复杂,所以让我们深入研究一些代码来理解它。
type Address struct {
city string
state string
}
type Person struct {
name string
age int
Address
}
在上面的代码段中,Person结构有一个匿名字段Address,它是一个结构。现在,Address的字段,即city和state,被称为promoted字段,因为可以像直接在Person结构本身中声明一样访问它们。
package main
import (
"fmt"
)
type Address struct {
city string
state string
}
type Person struct {
name string
age int
Address
}
func main() {
p := Person{
name: "Naveen",
age: 50,
Address: Address{
city: "Chicago",
state: "Illinois",
},
}
fmt.Println("Name:", p.name)
fmt.Println("Age:", p.age)
fmt.Println("City:", p.city) //city is promoted field
fmt.Println("State:", p.state) //state is promoted field
}
在上面程序的第29行和第30行中,可以访问提升字段city和state,就好像它们是使用语法p.city和p.state在结构p中声明的一样。此程序打印:
Name: Naveen
Age: 50
City: Chicago
State: Illinois
结构体导出
如果结构类型以大写字母开头,则它是导出类型,可以从其他包访问。类似地,如果结构的字段以caps开头,则可以从其他包访问它们。让我们编写一个具有自定义包的程序来更好地理解这一点。在Documents目录中创建名为structs的文件夹。请随意在任何您喜欢的地方创建它。我更喜欢我的文档目录。
mkdir ~/Documents/structs
创建一个gomod,并命名为structs
。
cd ~/Documents/structs/
go mod init structs
创建另外一个目录computer
申明一个结构体。
mkdir computer
创建一个spec.go
文件,并写入如下内容:
package computer
type Spec struct { //exported struct
Maker string //exported field
Price int //exported field
model string //unexported field
}
上面的代码片段创建了一个程序包计算机,其中包含一个导出的结构类型Spec,其中有两个导出的字段Maker和Price,以及一个未导出的字段模型。让我们从主包导入这个包并使用Spec结构。
创建名为main的文件。进入structs目录并在main.go中编写以下程序:
package main
import (
"structs/computer"
"fmt"
)
func main() {
spec := computer.Spec {
Maker: "apple",
Price: 50000,
}
fmt.Println("Maker:", spec.Maker)
fmt.Println("Price:", spec.Price)
}
这个结构体如下结构体:
├── structs
│ ├── computer
│ │ └── spec.go
│ ├── go.mod
│ └── main.go
在上面程序的第4行,我们导入计算机包。在第13行和第14行,我们访问struct Spec的两个导出字段Maker和Price。这个程序可以通过执行命令go-install,然后执行structs命令来运行。
go install
structs
运行之后如下结果:
Maker: apple
Price: 50000
如果我们试图访问未报告的字段模型,编译器会抱怨。更换main的内容。使用以下代码。
package main
import (
"structs/computer"
"fmt"
)
func main() {
spec := computer.Spec {
Maker: "apple",
Price: 50000,
model: "Mac Mini",
}
fmt.Println("Maker:", spec.Maker)
fmt.Println("Price:", spec.Price)
}
在上述程序的第12行中,我们尝试访问未报告的字段模型。运行此程序将导致编译错误。
# structs
./main.go:12:13: unknown field 'model' in struct literal of type computer.Spec
由于模型字段未报告,因此无法从其他包访问它。
结构体比较
结构是值类型,如果它们的每个字段都是可比较的,则可以进行比较。如果两个结构变量的对应字段相等,则认为它们相等。
package main
import (
"fmt"
)
type name struct {
firstName string
lastName string
}
func main() {
name1 := name{
firstName: "Steve",
lastName: "Jobs",
}
name2 := name{
firstName: "Steve",
lastName: "Jobs",
}
if name1 == name2 {
fmt.Println("name1 and name2 are equal")
} else {
fmt.Println("name1 and name2 are not equal")
}
name3 := name{
firstName: "Steve",
lastName: "Jobs",
}
name4 := name{
firstName: "Steve",
}
if name3 == name4 {
fmt.Println("name3 and name4 are equal")
} else {
fmt.Println("name3 and name4 are not equal")
}
}
在上面的程序中,名称结构类型包含两个字符串字段。由于字符串是可比较的,因此可以比较类型名的两个结构变量。在上面的程序中,name1和name2相等,而name3和name4不相等。此程序将输出:
name1 and name2 are equal
name3 and name4 are not equal
如果结构变量包含不可比较的字段,那么它们就不可比较(感谢reddit的alasija指出这一点)。
package main
import (
"fmt"
)
type image struct {
data map[int]int
}
func main() {
image1 := image{
data: map[int]int{
0: 155,
}}
image2 := image{
data: map[int]int{
0: 155,
}}
if image1 == image2 {
fmt.Println("image1 and image2 are equal")
}
}
在上面的程序中,图像结构类型包含类型映射的字段数据。地图是不可比较的,因此无法比较image1和image2。如果运行此程序,编译将失败并返回错误。
./prog.go:20:12: invalid operation: image1 == image2 (struct containing map[int]int cannot be compared)
来源:https://segmentfault.com/a/1190000042589069
猜你喜欢
- 1、首先介绍简单的try......except尝试运行的放例如下面的图和代码来简单介绍下:def test(x,y): pri
- 引言 本文通过python3、第三方python库Selenium和谷歌浏览器Chrome,完成WPS表单的自动填写。开发环境配置 py
- 一、背景描述csv格式文件是一种类似于excel的文件格式asc格式文件是一种可以用text打开的文本文件csv转asc本来可以用arcgi
- 数据修改主要以增删改差为主,这里比较几种写法在数据处理时间上的巨大差别。数据量大概是500万行级别的数据,文件大小为100M。1.iloci
- 删除链表中重复的结点: 定义两个指针pre和current两个指针同时往后移动,current指针如果与后一个结点值相同,就独自往前走直到没
- 前言日志是对于软件执行所发生的事件的一种追踪记录方式。日常使用过程中对代码执行的错误和问题会进行查看日志来分析定位问题所在。平常编写代码以及
- 一、MHA介绍(一)、什么是MHAMHA(MasterHigh Availability)是一套优秀的MySQL高可用环境下故障切换和主从复
- 在我们学习的过程中会遇到这么样的问题,就是在我们学习的过程中会发现需要分页处理,这里呢,给大家介绍书上说的分页。@app.route(
- 你还没用 jQuery 写过导航菜单? 相信看到这些出色的jQuery导航菜单后,一定会为此而后悔没早点把 jQuery 应用到自己的Web
- 所谓匿名函数,即是不需要定义函数,像表达式一样使用,不需要函数名(很多时候名字让我很困扰),一些简单的函数简单化, 举个例子我需要两个整数相
- 今天帮同事处理一个棘手的事情,问题是这样的:无论在客户机用哪个版本的mysql客户端连接服务器,发现只要服务器端设置了character-s
- 一、python魔法方法Python的魔法方法会在特定的情况下自动调用,且他们的方法名通常被双下划线包裹,之前我们学习的构造函数和析构函数就
- 一、Beautiful Soup的安装Beautiful Soup是Python的一个HTML或XML的解析库,使用它可以很方便地从网页中提
- 前言每一个好习惯都是一笔财富,本文分 SQL 后悔药,SQL 性能优化,SQL 规范优雅三个方向,分享写 SQL 的 21 个好习惯和最佳实
- 用于序列化的两个模块json:用于字符串和Python数据类型间进行转换pickle: 用于python特有的类型和python的数据类型间
- 1、仅 Safari 和 Opera 识别的 Hack@media all and (min-width:&n
- ubuntu 14.04 + python3.4 + chrome, 在浏览器中查看tensorboard, 发现出了graph,其他的数据
- [项目]数据库中users表,包含u_name,u_pwd两个字段,其中u_name存在重复项,现在要实现把重复的项删除![分析]1、生成一
- 最小编辑距离或莱文斯坦距离(Levenshtein),指由字符串A转化为字符串B的最小编辑次数。允许的编辑操作有:删除,插入,替换。具体内容
- 抽象类作用:抽象类就是控制子类的方法的名称,要求子类必须按照父类的要求的实现指定的方法,且方法名要和父类保持一致一、问题场景主要使用场景是这