Go Structs: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
No edit summary
No edit summary
Line 1: Line 1:
=External=
* https://go.dev/ref/spec#Struct_types
=Internal=
=Internal=
* [[Go_Concepts_-_The_Type_System#Structs|The Type System]]
=Overview=
=Overview=
Structs can represent data values that could have either a primitive or non-primitive value. "Primitive" structs imply creating a new instance every time an existing value is mutated. In this case, values themselves, rather than pointers, are used to share values of those structs. An example is the <code>Time</code> structure in the <code>[[Go Package time|time]]</code> package. However, in most cases, structs exhibit a non-primitive structure: adding or removing something from the value mutates the value, does not create a new value.


=TO DISTRIBUTE=
<font color=darkkhaki>
==Overview==
A <tt>struct</tt> is a user-defined type that contains named typed fields (and possibly other type names, see [[#Embedded_Types|embedded types]] below). <tt>struct</tt>s may also declare methods that operate on that data. The fields usually represent a ''has-a'' relationship, <tt>struct</tt> allows creating ''composite types''. <tt>struct</tt>s can also have [[Go_Concepts_-_Functions#Methods|methods]] associated with them.
A <tt>struct</tt> is a user-defined type that contains named typed fields (and possibly other type names, see [[#Embedded_Types|embedded types]] below). <tt>struct</tt>s may also declare methods that operate on that data. The fields usually represent a ''has-a'' relationship, <tt>struct</tt> allows creating ''composite types''. <tt>struct</tt>s can also have [[Go_Concepts_-_Functions#Methods|methods]] associated with them.


Line 16: Line 19:
</font>
</font>


=Declaration=
==Declaration==


The struct type definition is introduced by the <tt>type</tt> keyword, to indicated that this is a user-defined type, followed by the type name and the keyword <tt>struct</tt>. Each field has a name and a type. The type can be a built-in type or a user-defined type, such as another <tt>struct</tt> or an [[Go_Interfaces#Interfaces_as_Fields|interface]].
The struct type definition is introduced by the <tt>type</tt> keyword, to indicated that this is a user-defined type, followed by the type name and the keyword <tt>struct</tt>. Each field has a name and a type. The type can be a built-in type or a user-defined type, such as another <tt>struct</tt> or an [[Go_Interfaces#Interfaces_as_Fields|interface]].
Line 39: Line 42:
</pre>
</pre>


==Filed Tags==
===Filed Tags===


See
See
Line 47: Line 50:
</blockquote>
</blockquote>


=Initialization=
==Initialization==


Once the type has been defined, variables of that type can be declared using the long declaration or short declaration syntax, as shown below. More details about variable declaration can be found [[Go_Concepts_-_Lexical_Structure#Variable_Declarations|here]]:
Once the type has been defined, variables of that type can be declared using the long declaration or short declaration syntax, as shown below. More details about variable declaration can be found [[Go_Concepts_-_Lexical_Structure#Variable_Declarations|here]]:


==Long Variable Declaration==
===Long Variable Declaration===


<pre>
<pre>
Line 59: Line 62:
If no explicit initialization follows, all the struct's fields are initialized with their [[Go Concepts - The Type System#Zero_Value|zero value]].
If no explicit initialization follows, all the struct's fields are initialized with their [[Go Concepts - The Type System#Zero_Value|zero value]].


==Short Variable Declaration==
===Short Variable Declaration===


The short variable declaration uses the short variable declaration operator [[Go_Concepts_-_Operators#:.3D|:=]]
The short variable declaration uses the short variable declaration operator [[Go_Concepts_-_Operators#:.3D|:=]]


===Literal===
====Literal====


Struct literal initialization comes in two forms.
Struct literal initialization comes in two forms.
Line 92: Line 95:
</pre>
</pre>


====Embedded Literals====
=====Embedded Literals=====


When a field of a <tt>struct</tt> is another <tt>struct</tt>, embedded literals can be used in initialization:
When a field of a <tt>struct</tt> is another <tt>struct</tt>, embedded literals can be used in initialization:
Line 105: Line 108:
</pre>
</pre>


===<tt>new()</tt>===
====<tt>new()</tt>====


<tt>[[Go_Built-In_Function_new|new()]]</tt> initialization:
<tt>[[Go_Built-In_Function_new|new()]]</tt> initialization:
Line 115: Line 118:
Note <tt>new()</tt> returns a ''pointer'' to the structure and not the structure itself.
Note <tt>new()</tt> returns a ''pointer'' to the structure and not the structure itself.


=Fields=
==Fields==


Fields can be access with the <tt>[[Go_Concepts_-_Operators#.|.]]</tt> operator.  
Fields can be access with the <tt>[[Go_Concepts_-_Operators#.|.]]</tt> operator.  
Line 134: Line 137:
Even if the enclosing struct type is [[Go Concepts - Packages#Exported_Identifiers|exported]] by a package, not all fields are exported automatically, only those whose first character is an upper case letter. This behavior makes possible to have "private" fields in a public structure, when the structure is used outside its package.
Even if the enclosing struct type is [[Go Concepts - Packages#Exported_Identifiers|exported]] by a package, not all fields are exported automatically, only those whose first character is an upper case letter. This behavior makes possible to have "private" fields in a public structure, when the structure is used outside its package.


==Field Tags and Attributes==
===Field Tags and Attributes===


A field declaration may be followed by an optional string literal ''tag'', which becomes an ''attribute'' for all the fields in the corresponding field declaration. The tags are made visible through a reflection interface and take part in type identity for structs but are otherwise ignored.
A field declaration may be followed by an optional string literal ''tag'', which becomes an ''attribute'' for all the fields in the corresponding field declaration. The tags are made visible through a reflection interface and take part in type identity for structs but are otherwise ignored.
Line 148: Line 151:
In the example above, tags have been included to provide ''metadata'' the JSON decoding function needs to parse JSON content. Each tag maps a field name in the <tt>struct</tt> type to a filed name in the JSON document.
In the example above, tags have been included to provide ''metadata'' the JSON decoding function needs to parse JSON content. Each tag maps a field name in the <tt>struct</tt> type to a filed name in the JSON document.


=Embedded Types=
==Embedded Types==


An embedded type is initialized by declaring a name of a struct inside other <tt>struct</tt> - ''without associating it with a field''. An embedded type is sometimes referred to as ''anonymous field''. It models an ''is-a'' relationship. Example:
An embedded type is initialized by declaring a name of a struct inside other <tt>struct</tt> - ''without associating it with a field''. An embedded type is sometimes referred to as ''anonymous field''. It models an ''is-a'' relationship. Example:
Line 163: Line 166:
</pre>
</pre>


==Methods and Embedded Types==
===Methods and Embedded Types===


A pointer receiver method associated with the embedded type also works with the embedding type. For example, if there's a:
A pointer receiver method associated with the embedded type also works with the embedding type. For example, if there's a:
Line 183: Line 186:
In the code sequence above, the invocations <tt>bPtr.m1(...)</tt> and <tt>bPtr.A.m1(...)</tt> are equivalent.
In the code sequence above, the invocations <tt>bPtr.m1(...)</tt> and <tt>bPtr.A.m1(...)</tt> are equivalent.


=Empty Structs=
==Empty Structs==


An empty struct allocates zero bytes when values of this type are created. They are useful when a type (but not state) is needed. Example:
An empty struct allocates zero bytes when values of this type are created. They are useful when a type (but not state) is needed. Example:

Revision as of 00:29, 25 August 2023

External

Internal

Overview

Structs can represent data values that could have either a primitive or non-primitive value. "Primitive" structs imply creating a new instance every time an existing value is mutated. In this case, values themselves, rather than pointers, are used to share values of those structs. An example is the Time structure in the time package. However, in most cases, structs exhibit a non-primitive structure: adding or removing something from the value mutates the value, does not create a new value.

TO DISTRIBUTE

Overview

A struct is a user-defined type that contains named typed fields (and possibly other type names, see embedded types below). structs may also declare methods that operate on that data. The fields usually represent a has-a relationship, struct allows creating composite types. structs can also have methods associated with them.

Considerations on the primitive nature of structs are available here.

Questions

  • Are all users can define (in terms of types) structs, or there are other user-defined types?
  • What is the difference between the methods that have the type as receiver and the function declared as fields by the type?

Declaration

The struct type definition is introduced by the type keyword, to indicated that this is a user-defined type, followed by the type name and the keyword struct. Each field has a name and a type. The type can be a built-in type or a user-defined type, such as another struct or an interface.

The structs can be declare at package level or inside a function.

type myStruct struct {
    i int
    s string
}

Fields with the same types can be collapsed:

type myStruct struct {
    ...
    i, j, k int
   ...
}

Filed Tags

See

Filed Tag and Attributes

Initialization

Once the type has been defined, variables of that type can be declared using the long declaration or short declaration syntax, as shown below. More details about variable declaration can be found here:

Long Variable Declaration

var ms myStruct

If no explicit initialization follows, all the struct's fields are initialized with their zero value.

Short Variable Declaration

The short variable declaration uses the short variable declaration operator :=

Literal

Struct literal initialization comes in two forms.

1. All the values are specified on the same line:

ms := myStruct{i: 5, s: "something"}

or

2. Values are specified on separate lines:

ms := myStruct{
    i: 5, 
    s: "something",
}

Note the trailing comma when using the multi-line literal.

There is a shorter struct literal where the name of the fields are omitted, provided that the order is maintained:

ms := myStruct{5, "something"}
Embedded Literals

When a field of a struct is another struct, embedded literals can be used in initialization:

a := A {
    b: B {
       d: "something",
    }
    c : "something else",
}

new()

new() initialization:

msPtr := new(myStruct)

Note new() returns a pointer to the structure and not the structure itself.

Fields

Fields can be access with the . operator.

Note that the . operator works with a regular struct variable as well as with a pointer to a struct: the compiler knows how to compensate for both of these cases. Also see . used for field access.

ms := myStruct{1, "s"}
msPtr := new(myStruct)

// valid:
ms.i

// valid:
msPtr.i

Even if the enclosing struct type is exported by a package, not all fields are exported automatically, only those whose first character is an upper case letter. This behavior makes possible to have "private" fields in a public structure, when the structure is used outside its package.

Field Tags and Attributes

A field declaration may be followed by an optional string literal tag, which becomes an attribute for all the fields in the corresponding field declaration. The tags are made visible through a reflection interface and take part in type identity for structs but are otherwise ignored.

Example:

type A struct {
    name string `json:"name"`
}

In the example above, tags have been included to provide metadata the JSON decoding function needs to parse JSON content. Each tag maps a field name in the struct type to a filed name in the JSON document.

Embedded Types

An embedded type is initialized by declaring a name of a struct inside other struct - without associating it with a field. An embedded type is sometimes referred to as anonymous field. It models an is-a relationship. Example:

type A struct {
    f1 int
}

type B struct {
    A
    f2 int
}

Methods and Embedded Types

A pointer receiver method associated with the embedded type also works with the embedding type. For example, if there's a:

func (a *A) m1(...) {
    ...
}

m1() also works directly with B instances as such:

bPtr := new(B)
...
bPtr.m1(...)

In the code sequence above, the invocations bPtr.m1(...) and bPtr.A.m1(...) are equivalent.

Empty Structs

An empty struct allocates zero bytes when values of this type are created. They are useful when a type (but not state) is needed. Example:

// we declared an empty struct that defines the type "A"
type A struct{}