Go Language: Difference between revisions
Line 165: | Line 165: | ||
Variables are names associated with memory locations that store values. Variable name rules are described in the [[#Identifiers|Identifiers]] section. Each variable has a [[#Type|type]]. Because Go is a [[Programming_Languages_Concepts#Static_Typing_vs_Dynamic_Typing|statically typed language]], the type associate with a variable may not change after declaration. | Variables are names associated with memory locations that store values. Variable name rules are described in the [[#Identifiers|Identifiers]] section. Each variable has a [[#Type|type]]. Because Go is a [[Programming_Languages_Concepts#Static_Typing_vs_Dynamic_Typing|statically typed language]], the type associate with a variable may not change after declaration. | ||
Each variable defined in Go occupies a unique memory location. It is not possible to have two variables that share the same storage location. It is possible to have two variable that point to the same storage location, in case of [[Go_Pointers#Overview|pointer variables]], but this is not the same thing as sharing the same storage location. In other words, Go has [[Reference_Variables|no reference variables]]. | Each variable defined in Go occupies a unique memory location. It is not possible to have two variables that share the same storage location. It is possible to have two variable that point to the same storage location, in case of [[Go_Pointers#Overview|pointer variables]], but this is not the same thing as sharing the same storage location. In other words, Go has [[#Reference_Variables|no reference variables]]. | ||
==<span id='Reference_Variables'></span><span id='Reference_Variable'></span>(No) Reference Variables== | ==<span id='Reference_Variables'></span><span id='Reference_Variable'></span>(No) Reference Variables== | ||
Go does not have [[Variables,_Parameters,_Arguments#Reference|reference variables]]. Each variable defined in Go occupies a unique memory location, which contains the value of the variable. [[Go_Maps#Overview|Maps]], [[Go_Channels#Overview|channels]], etc. variables are all values, not references. As such, there is no pass-by-referece in Go, everything is passed by value. To achieve behavior similar to pass-by-reference, use pointers. This is explained here: {{Internal|Go_Functions#Pass-by-value_vs._pass-by-pointer_vs._pass-by-reference|Pass by Value vs. Pass by Reference vs. Pass by Pointer in Go}} | Go does not have [[Variables,_Parameters,_Arguments#Reference|reference variables]]. Each variable defined in Go occupies a unique memory location, which contains the value of the variable. [[Go_Maps#Overview|Maps]], [[Go_Channels#Overview|channels]], etc. variables are all values, not references. As such, there is no pass-by-referece in Go, everything is passed by value. To achieve behavior similar to pass-by-reference, use pointers. This is explained here: {{Internal|Go_Functions#Pass-by-value_vs._pass-by-pointer_vs._pass-by-reference|Pass by Value vs. Pass by Reference vs. Pass by Pointer in Go}} |
Revision as of 01:40, 29 September 2023
External
- The Go Programming Language Specification https://go.dev/ref/spec
- go.dev Documentation https://go.dev/doc/#learning
- Search packages or symbols https://pkg.go.dev
Internal
- Go
- Python Language
- Microservices
- Go Language | Modularization
- Go Language | Functions
- Go Language | Object Oriented Programming
- Go Language | Error Handling
- Go Language | Concurrency with Goroutines
Overview
Go Language Specification document defines Go as a general-purpose compiled language designed with systems programming in mind. It is statically and strongly typed and garbage-collected and has explicit support for concurrent programming. Programs are constructed from packages, whose properties allow efficient management of dependencies. The existing implementations use a traditional compile/link model to generate executable binaries.
Go declarations can be read "naturally" from left to right, which makes it easy to read.
These are several reasons to use Go:
- It compiles fast, and it runs fast.
- Concurrency is a fundamental part of the language and it is efficient.
- The standard library has almost everything one needs.
- It is a terse language and "feels" dynamically typed, but it compiles straight into machine code.
- It has garbage collection.
Printing
Use println()
:
println("something")
Also see:
Comments
C-style comments, dropped by the compiler:
// This is a comment.
var x int // this is another comment
Block comments:
/*
This is a
block comment.
*/
Comments should be complete sentences and end with a period.
Every exported package member, and the package itself should have a comment. A comment immediately preceding the package
declaration is considered the documentation comment for the package as a whole. Longer package comments may warrant a file of their own. The file is usually called doc.go
.
The first sentence of the comment should start with the name being exported and provide a summary of it. Function parameters and other identifiers are mentioned without quotation or markup.
Also see:
Whitespace
Whitespace in Go are carriage returns (u+000D), newlines (u+000A), spaces (u+0020) and tabs (u+0009).
Keywords
Keywords, or reserved words, can only be used to mean the thing Go expects them to mean. They cannot be used as identifiers: variable names, function names, or as names of other constructs.
Also see:
Identifiers (Names)
Names start with a letter. They can have any number of letters, digits and underscores. They are case sensitive, and their spelling and capitalization must be different of that of the keywords of the language.
Go style recommends camel-case for identifiers.
The Blank Identifier (_)
_
is the blank identifier. The blank identifier is used to tell the compiler that we want to ignore one return value of a function when we're only interested in others, that we don't need a variable or that we want to import a package that is not directly used.
The blank identifier can never be referenced.
Pre-Declared Identifiers
Pre-declared identifiers are available by default, without needing to import anything. They are implicitly declared in the universe block.
Pre-Declared Zero Value
Pre-Declared Constants
Pre-Declared Types
The source code of these types is available in $GO_HOME/src/builtin/builtin.go
.
Pre-Declared Functions
TODO: when the similar table from Go_Functions#Built-in_Functions is fully updated, copy it here for convenience.
Also see:
Exported Identifiers
An identifier must be exported to permit access to it from another package. An identifiers is exported if the first character of the identifier's name is a Unicode uppercase letter AND the identifier is declared in the package block or it is a field name or a method name. For more details about package encapsulation see:
Blocks
A block is a contiguous sequence of declarations and statements. They can be enclosed in matching curly brackets, in which case they are called explicit blocks, or not, and in that case they're called implicit blocks.
Blocks are hierarchical: blocks can be enclosed in other, larger blocks, recursively.
Blocks are involved when the scope of a variable is determined. Each block can have its own variables, and the visibility rules for a specific variable are defined in relation to blocks.
Explicit Block
An explicit block is a sequence of statements enclosed within curly brackets.
{
// block
...
}
A function's body is defined as an explicit block.
Implicit Block
There are blocks implicitly defined without curly brackets. From the "largest" to "smaller" implicit blocks, there are the Universe block, Package blocks, File blocks and others.
Universe Block
All Go code in existence.
Package Block
Each package has a package block, which contains all the code in the package. All identifiers from the package block whose names start with an uppercase letter are exported.
File Block
All Go source code in a single file.
if, for, switch Blocks
switch and select Clauses Blocks
Variables
Variables are names associated with memory locations that store values. Variable name rules are described in the Identifiers section. Each variable has a type. Because Go is a statically typed language, the type associate with a variable may not change after declaration.
Each variable defined in Go occupies a unique memory location. It is not possible to have two variables that share the same storage location. It is possible to have two variable that point to the same storage location, in case of pointer variables, but this is not the same thing as sharing the same storage location. In other words, Go has no reference variables.
(No) Reference Variables
Go does not have reference variables. Each variable defined in Go occupies a unique memory location, which contains the value of the variable. Maps, channels, etc. variables are all values, not references. As such, there is no pass-by-referece in Go, everything is passed by value. To achieve behavior similar to pass-by-reference, use pointers. This is explained here:
Variable Declaration
The name and the type of the variable are specified in the variable declaration, which is a statement prefixed by the keyword var
. This style is some times referred to as "long declaration":
var <variable-name>[, <variable-name-2>, ...] <type>
var size int
var size, weight int
Such a variable declaration yields an initialized variable, which, in fact, is initialized to zero values.
Also see:
Variable Names
For rules and conventions to use when naming variables, see Identifiers (Names) above. For special rules concerning pointer variable names, see:
Variable Initialization after Declaration
var color string
color = "blue"
Variable Initialization in Declaration
Variables can be initialized in the declaration:
var color string = "blue"
This is redundant, and some editors will render the type name in gray and pop up a soft static analysis warning: "Type can be omitted", because an alternative syntax, initialization with type inference is available.
Variable Initialization with Type Inference
var color = "blue"
Idiomatic Variable Naming Conventions
Variable names should be short rather than long. The closer to declaration a name is used, the shorter it should be. i
is fine when iterating over an array.
Since references include package names, variable names should not be prefixed with the package name. If the package name is xml
, use the name Reader
, not XMLReader
. In the second case, the full name would be xml.XMLReader
.
Also see:
Swap Variable Values
a, b = b, a
Also works with arrays and slices.
Short Variable Declaration
The declaration and initialization can be performed together with the :=
operator. This obviates the need for the var
keyword and it deploys type inference. This kind of declaration can only be performed inside a function.
color := "blue"
The short variable declaration can be used to initialize more than one variable at the same time, when the assignment contains multiple values, or when on the right side of the assignment there is a function that returns multiple values:
i, j := 0, 1
color, size := someFuncWithTwoReturnValues(...)
Variable Declaration with new()
new()
creates a variable or a certain type, specified as the argument of the new()
function invocation, and returns a pointer pointer to that variable.
ptri := new(int)
*ptri = 3
Why do we need to involve variables here? There's no variable, new() just allocates space for an instance of a certain type and returns a pointer to it.
Creating instances with New()
Scope
The scope of a variable is defined as places in the code where the variable can be accessed. In Go, the scope of a variable can be formally defined by using the concept of block: Go is a lexically scoped language using blocks.
Given two blocks bi and bj, we say that bi ≥ bj if bj is inside (is enclosed in) bi. This relationship is transitive.
The formal definition of visibility (or accessibility) of a variable v
is: a variable v
is visible in a block bj if it is declared in a block bi so bi ≥ bj.
If a variable with the same name is declared in two blocks between which there is a an inclusion relationship, the variable defined in the "closest" block takes precedence.
Also see:
Variable Deallocation
Constants
A constant is a typed expression whose value is known at compile time, and cannot be changed once declared. The compiler detects modification attempts and throws a compilation error. The type is inferred from the right-hand side of the assignment. Multiple constants can be assigned at the same time using enclosing parentheses.
const <constant_identifier> [constant_type] = <initial_value_literal>
const a = 1
const (
b = 1.1
c = "something"
)
Enumerations
Go does not have formal enums, but the language allows for sets of related, but distinct constants. They represent a property that has several distinct possible values, like the days of the weeks or the months of the year. They are declared using the pre-declared constant iota
:
type DayOfTheWeek int
const (
MON DayOfTheWeek = iota
TUE
WED
THU
FRI
SAT
SUN
)
Type
A Go type is similar to types from other languages, in that it defines the range of values and the operations that can be performed with all those values. For more details, see:
Go is a strongly and statically typed language with no implicit conversions. This gives Go a stronger type safety than Java, which as implicit conversions, but the code reads more like Python, which has untyped variables.
To deplete Go Concepts - The Type System
The Primitive vs. Non-Primitive Nature of Types
A type is said to have a primitive nature if adding something to, or removing something from a value of that type creates a new value. When passing values of these types to functions, copies the arguments are made inside the function. Integers, floats, booleans and strings are primitive types. Primitive types are some times referred to as data types. The pointers themselves, but not, in general, the instances the pointers point to, are also a primitive type. The Go language specification says that pointers are a composite type.
For non-primitive types, adding or changing something to the value of the type does not create a new value, it instead mutates the existing value. The value itself is changed, while various references in existence point to the mutated value. When such a value is passed to a function, no copy of the value is made, a reference to it is passed instead. When a factory function returns a pointer, it's a good indication that the nature of the value being returned is non-primitive.
Structs can represent data values that could have either a primitive or a non-primitive value. For a discussion on this subject, see Structs below.
More details: Go in Action Section 5.3 "The Nature of Types".
Also see:
Type Declaration
Go allows defining a type alias, or alternate name, for a type. A type declaration is a statement that begins with the keyword type
:
type Size int
The type
keyword is also used for declaring structs and interfaces.
Primitive (Data) Types
Pointers
String
Numbers
Integer
Floating Point
Booleans
Arrays
In Go, arrays have characteristics of a primitive data type, they are handled as values, and they are passed to function by value. Arrays are at the same time composite types. For more details, see:
Composite Types
Go composite types aggregate other data types. In Go, the composite data types are arrays, slices, maps, structs, pointers, functions, interfaces and channels.
Arrays
Slices
Maps
Functions
Channels
type-Introduced Types
Structs
Interfaces
Reference Type
TODO: Go_Concepts_-_The_Type_System#Reference_Types
Type Conversion
Built-in functions are available to do type conversions. The general syntax is:
newTypeVar = <type-name>(oldTypeVar)
var x int32 = 1
var y int16 = 2
// println(x + y) // compilation will fail on this, cannot apply + on two different types
z := int32(y)
println(x + z)
Zero Value
Pass by Value vs. Pass by Reference vs. Pass by Pointer in Go
Concrete vs. Interface Types
A concrete type is a regular type that is fully specified: it specifies the exact representation of its data, and for every method that declares it as its receiver type, there is implementation. On the other hand, an interface type is the type resulted from declaring an interface. The interface type does not have data, nor method implementations.
An interface type gets mapped to a concrete type.
Expressions
operators combine operands into expressions.
Operators
* | Multiplication, Pointer Dereference | * has several meanings in Go:- Multiplication operator. - Dereferencing operator. For more details see Dereferencing Operator. - * also designates pointer types and pointer array types.
|
[] | Indexing operator | Used with strings, arrays, slices and maps. |
<- | Arrow operator | Used to send and receive data to/from a channel. |
Operator Precedence
The operators with the highest precedence appear at the top:
Category | Operator | Associativity |
---|---|---|
Postfix | Left to Right | |
Unary | Right to left | |
Multiplicative | Left to right | |
Additive | Left to right | |
Shift | Left to right | |
Relational | Left to right | |
Equality | Left to right | |
Bitwise AND | Left to right | |
Bitwise XOR | Left to right | |
Bitwise OR | Left to right | |
Logical AND | Left to right | |
Logical OR | Left to right | |
Assignment | Right to Left | |
Comma | Left to right |
Control Flow
Control flow defines the order in which statements are executed in a program. The control flow can be changed by using control structures.
If Statement
The if
statement specifies the conditional execution of two branches according to the value of a boolean expression. Optionally, the boolean expression may be preceded by another statement, which is executed before the expression is evaluated.
Simple If
if <condition> {
<statements>
}
if x > 5 {
println(x)
}
If/else
if <condition> {
<statements>
} else {
<statements>
}
if x > 5 {
println(x)
} else {
println("something else")
}
A special if
syntax supports the Go error handling idiom that relies on functions returning errors as result value:
if result, err := f(); err {
// handle error
...
} else {
// handle success
result ...
}
If/else if/else
if <condition> {
<statements>
} else if <condition> {
<statements>
} else {
<statements>
}
if x < 5 {
println(x)
} else if x == 5 {
println("is 5")
} else {
println("something else")
}
For Loops
The most generic syntax of the for
statement is:
for <init>; <condition>; <update> {
<statements>
}
The for
loop executes the init
statement and then iterates while the condition
evaluates to true. The condition
is an expression evaluated on each iteration. condition
must evaluate to a boolean. If the expression evaluates to true, the block is executed. At the end of each block execution, the update
statement is executed.
The init
statement is usually a variable declaration and initialization. Multiple variables can be declared at the same time (for more details, see Short Variable Declaration section above):
for i, j := 0, 0; ... {
...
}
Examples:
for i := 0; i < 3; i ++ {
println(i)
}
All three expressions (initialization, condition and update) are optional:
i := 0
for i < 3 {
println(i)
i ++
}
i := 0
for {
if i == 3 {
break
}
println(i)
i ++
}
Break
break
exits the closest enclosing loop.
Continue
continue
skips the rest of the current iteration.
Switch
Tagless Switch
Error Handling
Modularization
In Go, programs are constructed from packages. More details:
Object Oriented Programming
Concurrency
Memory Management and Garbage Collection
Extension
What is an extension?
Assertions
Go does not have assertions. This is why: https://golang.org/doc/faq#assertions
A crude equivalent would be:
if condition {
panic(...)
}
Compilation
Go compiler is fast, for the following reasons:
- All imports must be explicitly listed at the beginning of each source file, so the compiler does not have to read and process the entire file to determine its dependencies.
- The dependencies of a package for a directed acyclic graph - this is enforced - and because there are no cycles, the packages can be compiled separately following a topological sort, possibly in parallel.
- The object file for a compiled Go package records export information not for just the package itself, but for all its dependencies. When compiling a package, the compiler must read one object file for each import, but need not look beyond these files.
Cross-Compilation
Set GOOS
and GOARCH
environment variables to appropriate values, during the build.
Build Tags
Special comments called build tags give more control over compilation
// +build linux darwin
For more details on build constraints, see:
go doc go/build
Object File
The result of the compilation of all source files that constitute a package is stored in an object file. Each package has one object file. Object files have an .a
extension. Package object files can be created and locally installed with go install
. If the linker detects a main
package, all interdependent object file are linked to produce the executable.
Incremental Builds
An incremental build is a Go compiler feature that allows it to build only the packages that have been changed or are affected by changes, instead of recompiling the entire project code base every time the project is compiled. This behavior is implemented by computing source file hashes and associating them with timestamps. The hashes and the object files produced by builds are stored in a build cache.