Структура – это коллекция типизированных полей, т.е. у кождого поля в структуре есть свой тип данных.
Создать структуру можно следующим образом:
type Example struct { FirstField string SecondField string ThirdField int }
Тут у структуры с именем Example есть коллекция полей – поля FirstField и SecondField типа string
, и поле ThirdField типа integer
.
Если различные поля имеют одинаковый тип – можно упростить объявление до такого вида:
type Example struct { FirstField, SecondField string ThirdField int }
Содержание
Объявление и инициализация структуры
Объявление переменной типа struct
Можно выполнить объявление структуры через переменную:
var st = Example
В таком случае поля структуры будут инициализированы с нулями:
package main import ( "fmt" ) type Example struct { FirstField, SecondField string ThirdField int } var st Example func main() { fmt.Println(st) }
Результат:
[simterm]
$ go run structs.go { 0}
[/simterm]
Либо выполнить объявление и проиницилизировать сразу с данными:
package main import ( "fmt" ) type Example struct { FirstField, SecondField string ThirdField int } var st1 Example var st2 = Example{"FirstValue", "SecondValue", 3} func main() { fmt.Println(st1) fmt.Println(st2) }
Обратите внимание, что при объявлении переменной st2 с иницилизацией данных в полях структуры – используется “=
“.
Проверяем:
[simterm]
$ go run structs.go { 0} {FirstValue SecondValue 3}
[/simterm]
Инициализацию можно выполнить, используя именованные поля. В таком случае поля, для которых значение не задано, будут проинициализированы со значением 0.
Для удобства чтения – значения можно разбивать построчно, разделяя их запятыми (в том числе после последнего элемента), как в примере с st3:
package main import ( "fmt" ) type Example struct { FirstField, SecondField string ThirdField int } var st1 Example var st2 = Example{"FirstValue", "SecondValue", 3} var st3 = Example{ FirstField: "FirstValue", ThirdField: 3, } func main() { fmt.Println(st1) fmt.Println(st2) fmt.Println(st3) }
Результат:
[simterm]
$ go run structs.go { 0} {FirstValue SecondValue 3} {FirstValue 3}
[/simterm]
И, как и с обычными переменными, внутри функций можно пропустить использование var
, и использовать “:
“:
package main import ( "fmt" ) type Example struct { FirstField, SecondField string ThirdField int } var st1 Example var st2 = Example{"FirstValue", "SecondValue", 3} func main() { st3 := Example{ FirstField: "FirstValue", ThirdField: 3, } fmt.Println(st1) fmt.Println(st2) fmt.Println(st3) }
Доступ к данным
Доступ к полям структуры
К отдельным полям структуры можно получить доступ с помощью точки:
package main import ( "fmt" ) type Example struct { FirstField, SecondField string ThirdField int } func main() { st := Example{ FirstField: "FirstValue", ThirdField: 3, } fmt.Println(st.FirstField) }
Результат:
[simterm]
$ go run structs.go FirstValue
[/simterm]
Переопределение значений полей
Что бы изменить уже проиницилизированное значение поля – достаточно его указать ещё раз:
... func main() { st := Example{ FirstField: "FirstValue", ThirdField: 3, } st.FirstField = "AnotherValue" fmt.Println(st.FirstField) }
Результат:
[simterm]
$ go run structs.go AnotherValue
[/simterm]
Указатели к структуре
Для доступа к стуктуре и полям можно использовать указатели (см. C: указатели – подробный разбор).
По умолчанию Go использует передачу аргументов в функцию по значению, а не по ссылке.
Что бы продемонстрировать разницу – добавим новую функцию, и выполним передачу по значению:
package main import ( "fmt" ) type Example struct { FirstField, SecondField string ThirdField int } func changeVal(changed_st Example) { // change passed struct's 'FirstField' field value changed_st.FirstField = "ChangedValue" // print value fmt.Println("\nchanged_st.FirstField value:", changed_st.FirstField) // print changed_st.FirstField address fmt.Printf("changed_st.FirstField address: %p\n", &changed_st.FirstField) } func main() { // initialize the 'st' variable with default value 'FirstValue' st := Example{ FirstField: "FirstValue", ThirdField: 3, } // redefine the st's FirstField with the new value 'AnotherValue' st.FirstField = "AnotherValue" // execute the changeVal() function and pass the 'st' variable/struct to change the 'FirstField's value changeVal(st) // print value fmt.Println("\nst.FirstField value:", st.FirstField) // print variable's address fmt.Printf("st.FirstField address: %p\n\n", &st.FirstField) }
Тут:
- в
st.FirstField = "AnotherValue"
задаём значение поля FirstField структуры Example равным AnotherValue changeVal(st)
– передаём стуктуру и её поля в функциюchangeVal()
по значению- выводим значение поля FirstField стуктуры Example
Проверяем:
[simterm]
$ go run structs.go changed_st.FirstField value: ChangedValue changed_st.FirstField address: 0xc00005c180 st.FirstField value: AnotherValue st.FirstField address: 0xc00005c150
[/simterm]
В результате – в выводе значения st.FirstField мы получаем то же, что было задано в рамках функции main()
, хотя в функции changeVal()
мы его изменили.
При этом – обращаем внимание на адреса переменных в обеих функциях:
- в
main()
– 0xc00005c150 - в
changeVal()
– 0xc00005c180
Теперь изменим код, и выполним передачу аргумента в функцию changeVal()
по ссылке:
... func changeVal(changed_st *Example) { // change passed struct's 'FirstField' field value changed_st.FirstField = "ChangedValue" // print value fmt.Println("\nchanged_st.FirstField value:", changed_st.FirstField) // print changed_st.FirstField address fmt.Printf("changed_st.FirstField address: %p\n", &changed_st.FirstField) } func main() { // initialize the 'st' variable with default value 'FirstValue' st := Example{ FirstField: "FirstValue", ThirdField: 3, } // redefine the st's FirstField with the new value 'AnotherValue' st.FirstField = "AnotherValue" // execute the changeVal() function and pass the 'st' variable/struct to change the 'FirstField's value changeVal(&st) // print value fmt.Println("\nst.FirstField value:", st.FirstField) // print variable's address fmt.Printf("st.FirstField address: %p\n\n", &st.FirstField) }
Тут мы поменяли:
changeVal(&st)
– выполняем передачу адреса переменной st, которая ссылается на Examplefunc changeVal(changed_st *Example)
– присваиваем переменной changed_st переданный адрес структуры Example
Результат:
[simterm]
$ go run structs.go changed_st.FirstField value: ChangedValue changed_st.FirstField address: 0xc00005c150 st.FirstField value: ChangedValue st.FirstField address: 0xc00005c150
[/simterm]
Теперь обе функции – и main()
, и changeVal()
– ссылаются на один и тот же адрес в памяти – 0xc00005c150.
Функция changeVal()
выполнила изменение в самой структуре, а не локальной копии changed_st.FirstField, и в функции main()
мы получаем значение ChangedValue, т.к. в локальной копии st.FirstField = "AnotherValue"
изменение выполняется до того, как в changeVal()
было выполнено изменение поля самой структуры.
Указатели и функция new()
Можно создать указатель на структуру с помощью функции new()
.
Изменим наш код:
... func changeVal(changed_st *Example) { // change passed struct's 'FirstField' field value changed_st.FirstField = "ChangedValue" // print value fmt.Println("\nchanged_st.FirstField value:", changed_st.FirstField) // print changed_st.FirstField address fmt.Printf("changed_st.FirstField address: %p\n", &changed_st.FirstField) } func main() { // create a new memory object // the "st"'s variable's value will be this object's address st := new(Example) fmt.Printf("\nThe st's variable type: %T\n", st) // init fileds values st.FirstField = "FirstValue" st.ThirdField = 3 // execute the changeVal() function and pass the "st" variable/struct to change the "FirstField"'s value changeVal(st) // print value fmt.Println("\nst.FirstField value:", st.FirstField) // print variable's address fmt.Printf("st.FirstField address: %p\n\n", &st.FirstField) }
changeVal()
оставляем без изменений, а переменной st присваиваем значение, которое вернёт функция new()
, которой мы передаём структуру Example – это будет указатель на созданный объект:
[simterm]
$ go run structs.go The st's variable type: *main.Example changed_st.FirstField value: ChangedValue changed_st.FirstField address: 0xc000070150 st.FirstField value: ChangedValue st.FirstField address: 0xc000070150
[/simterm]
Експорт структур и полей
Если имя структуры или поля в ней начинается с заглавной буквы – его можно использовать как экпортированное в других модулях.
Проверяем $GOPATH
:
[simterm]
$ go env GOPATH /home/setevoy/Scripts/Go/goprojects
[/simterm]
Переходим туда, каталог src
, и создаём пакет example
:
[simterm]
$ cd /home/setevoy/Scripts/Go/goprojects/src && mkdir example
[/simterm]
Создаём модули – modename
и main
:
[simterm]
$ cd example && mkdir {modename,main}
[/simterm]
Создаём файлы, пока пустые:
[simterm]
$ touch main/main.go && touch modename/example.go
[/simterm]
Проверяем структуру каталогов и файлов:
[simterm]
$ tree ~/Scripts/Go/goprojects/src/example/ /home/setevoy/Scripts/Go/goprojects/src/example/ ├── main │ └── main.go └── modename └── example.go
[/simterm]
Создаём модуль modename
– в файле example.go
создаём две структуры:
package modename type Exported struct { ExportedField1 string unexportedField1 string } type unexported struct { ExportedField1 string unexportedField2 string }
И файл main/main.go
:
package main import ( "example/modename" "fmt" ) func main() { fmt.Println(modename.Exported{ExportedField1: "Value"}) }
Проверяем:
[simterm]
$ go run main/main.go {Value }
[/simterm]
Пробуем использовать поле unexportedField1:
func main() { fmt.Println(modename.Exported{unexportedField1: "Value"}) }
Аналогично – попытка использовать структуру unexported тоже приведёт к ошибкам:
... func main() { fmt.Println(modename.unexported{ExportedField1: "Value"}) }
Результат:
[simterm]
$ go run main/main.go # command-line-arguments main/main.go:10:14: cannot refer to unexported name modename.unexported main/main.go:10:14: undefined: modename.unexported
[/simterm]
Поле unexportedField2 структуры unexported вернёт такую же ошибку.
Теги в структурах
Собственно, весь этот пост появился из-за того, что мне стало интересно – что за “`
” в некоторых структурах, например – в go-flags
.
Объявление полей в структурах может дополняться строковыми литералами – тегами, которые потом могут использоваться в текущем пакете, или экспортированном.
Для примера возьмём такой код:
package main import ( "fmt" "reflect" ) type Example struct { // declare field with the "tagname" tag and its "tagvalue" value Field string `tagname:"tagvalue"` } func main() { // initialize Example struct's fields st := Example{Field: "Value1"} // access tags via the "reflect" package tp := reflect.TypeOf(st) // set the Field's tag and value to the "res" variable res, _ := tp.FieldByName("Field") // all field's data fmt.Println("All filed's data:", res) fmt.Printf("Data type: %T\n", res) // tag's name and value fmt.Println("Field's tag data: ", res.Tag) // tag's value only using the Get() fmt.Println("Tag's value with Get(): ", res.Tag.Get("tagname")) // tag's value only using the Lookup() value, ok := res.Tag.Lookup("tagname") fmt.Printf("Tag's value and return code with Lookup(): %s, %t\n", value, ok) }
Тут:
- инициализируем поле структуры Example, присваиваем её переменной st
- используя
TypeOf()
выполняем рефлексию структуры - используя
FieldByName()
– получаем поле “Field” структуры “Example“, типStructTag
- выводим все содержимое поля из типа
StructTag
- выводим только содержимое его тега(ов)
- получаем только значение тега, используя
Get()
- и аналогично, но используя
Lookup()
Результат:
[simterm]
$ go run struct_tags.go All filed's data: {Field string tagname:"tagvalue" 0 [0] false} Data type: reflect.StructField Field's tag data: tagname:"tagvalue" Tag's value with Get(): tagvalue Tag's value and return code with Lookup(): tagvalue, true
[/simterm]
Ссылки по теме
Golang Structs Tutorial with Examples
Pass by pointer vs pass by value in Go