Go For C++ Programmers
+ ++Go is a systems programming language intended as an alternative to C++. +These are some notes on Go for experienced C++ programmers. This +document discusses the differences between Go and C++, and says little +to nothing about the similarities. + +
+For a more general introduction to Go, see the +Go tutorial. + +
+For a detailed description of the Go language, see the +Go spec. + +
+There is more documentation about go. + + + +
+ +Conceptual Differences
+ +-
+
- Go does not have classes with constructors or destructors. + Instead of class methods, a class inheritance hierarchy, + and virtual functions, Go provides interfaces, which are + discussed in more detail below. + Interfaces are also used where C++ uses templates. + +
- Go uses garbage collection. It is not necessary (or currently possible) + to explicitly release memory. The garbage collection is (intended to be) + incremental and highly efficient on modern processors. + +
- Go supports pointers, but does not support pointer arithmetic. You cannot + use a pointer variable to walk through the bytes of a string. + +
- Arrays in Go are first class values. When an array is used as a + function parameter, the function receives a copy of the array, + not a pointer to it. However, in practice functions often use + slices for parameters, rather than arrays. This is discussed further + below. + +
- Strings are provided by the language. They may not change once they + have been created. + +
- Hash tables are provided by the language. They are called maps. + +
- Processes, and communication channels between them, are provided by + the language. This is discussed further below. + +
- Certain types (maps, channels, and slices, all described further below) + are passed by reference, not by value. That is, passing a map to a + function does not copy the map, and if the function changes the map + the change will be seen by the caller. + +
- Go does not use header files. Instead, each source file is part of a + defined package. When a package defines an object + (type, constant, variable, function) with a name which starts with an + uppercase letter, than object is visible to any other file which + imports that package. + +
- Go does not support implicit type conversion. Operations that mix + different types require casts (called conversions in Go). + +
- Go does not support function overloading and does not support user + defined operators. + +
- Go does not support
const
orvolatile
qualifiers. + + - Go uses
nil
for invalid pointers, where C++ uses +NULL
or simply0
. +
Syntax
+ ++The declaration syntax is reversed compared to C++. You write the name +followed by the type. Unlike C++, the syntax for a type does not match +the way in which the variable is used. Type declarations may be read +easily from left to right. + +
+Go C++ +var v1 int; // int v1; +var v2 string; // approximately const std::string v2; +var v3 [10]int; // int v3[10]; +var v4 []int; // approximately int* v4; +var v5 struct { f int }; // struct { int f; } v5; +var v6 *int; // int* v6; // but no pointer arithmetic +var v7 map[string]int; // approximately unordered_map<string, int>* v7; +var v8 func(a int) int; // int (*v8)(int a); ++ +
+Declarations generally take the form of a keyword followed by the name
+of the object being declared. The keyword is one of var
,
+func
,
+const
, or type
. Method declarations are a minor
+exception in that
+the receiver appears before the name of the object begin declared; see
+the discussion of interfaces.
+
+
+You can also use a keyword followed by a series of declarations in +parentheses. + +
+var (i int; m float) ++ +
+When declaring a function, you must provide a name for each parameter +or not provide a name for any parameter; you can't omit some names +and provide others. You may group several names with the same type: + +
+func f (i, j, k int); ++ +
+A variable may be initialized when it is declared. When this is done, +specifying the type is permitted but not required. When the type is +not specified, the type of the variable is the type of the +initialization expression. + +
+var v = *p; ++ +
+See also the discussion of constants, below. +If a variable is not initialized, the type must be specified. +In that case it will be +implicitly initialized to 0 (or nil, or whatever). There are no +uninitialized variables in Go. + +
+Within a function, a simple declaration syntax is available with
+:=
.
+
+
+v1 := v2; ++ +
+This is equivalent to + +
+var v1 = v2; ++ +
+Go permits multiple assignments which are done in parallel. + +
+i, j = j, i; // Swap i and j. ++ +
+Functions may have multiple return values, indicating by a list in +parentheses. + +
+func f() (i int, j int); +v1, v2 = f(); ++ +
+Go treats semicolons as separators, not terminators. Moreover,
+a semicolon
+is not required after a curly brace ending a type declaration (e.g.,
+var s struct {}
) or a block. Semicolons are never required at the
+top level of a file (between global declarations). However, they are
+always permitted at
+the end of a statement, so you can continue using them as in C++.
+
+
+Go treats semicolons as separators, not terminators. Moreover,
+a semicolon
+is not required after a curly brace ending a type declaration (e.g.,
+var s struct {}
) or a block. Semicolons are never required at the
+top level of a file (between global declarations). However, they are
+always permitted at
+the end of a statement, so you can continue using them as in C++.
+
+
+When using a pointer, you use .
instead of ->
.
+Thus syntactically
+speaking there is no difference between a structure and a pointer to a
+structure.
+
+
+type my_struct struct { i int } +var v9 my_struct; // v9 has structure type +var p9 *my_struct; // p9 is a pointer to a structure +f(v9.i, p9.i) ++ +
+Go does not require parentheses around the condition of a if
+statement, or the expressions of a for
statement, or the value of a
+switch
statement. On the other hand, it does require curly braces
+around the body of an if
or for
statement.
+
+
+if a < b { f() } // Valid +if (a < b) { f() } // Valid +if (a < b) f(); // INVALID ++ +
+Go does not have a while
statement nor does it have a
+do/while
+statement. The for
statement may be used with a single condition,
+which makes it equivalent to a while
statement. Omitting the
+condition entirely is an endless loop.
+
+
+Go permits break
and continue
to specify a label.
+The label must
+refer to a for
, switch
, or select
+statement.
+
+
+In a switch
statement, case
labels do not fall
+through. You can
+make them fall through using the fallthrough
keyword. This applies
+even to adjacent cases.
+
+
+switch i { case 0: case 1: f() } // f is not called when i == 0! ++ +
+But a case
can have multiple values.
+
+
+switch i { case 0, 1: f() } // f is called if i == 0 || i == 1. ++ +
+The values in a case
need not be constants - or even integers;
+any type
+that supports the equality comparison operator, such as strings or
+pointers, can be used - and if the switch
+value is omitted it defaults to true
.
+
+
+switch { case i < 0: f1() case i == 0: f2() case i > 0: f3() } ++ +
+The ++
and --
operators may only be used in
+statements, not in expressions.
+You cannot write c = *p++
. *p++
is parsed as
+(*p)++
.
+
+
Constants
+ +
+In Go integer and floating-point constants have so-called ideal types.
+This applies even to constants named with a const
declaration,
+if no
+type is given in the declaration. An ideal type becomes concrete when
+it is actually used. This permits constants to be used relatively
+freely without requiring general implicit type conversion.
+
+
+var a uint; f(a + 1) // Ideal type of "1" becomes "uint". ++ +
+The language does not impose any limits on the size of an abstract +integer constant or constant expression. A limit is only applied when +a constant expression is used where a type is required. + +
+const huge = 1 << 100; f(huge >> 98) ++ +
+Go does not support enums. Instead, you can use the special name
+iota
in a single const
declaration to get a
+series of increasing
+value. When an initialization expression is omitted for a const
,
+it reuses the preceding expression.
+
+
+const ( red = iota; blue; green ) // red == 0, blue == 1, green == 2 ++ +
Slices
+ +
+A slice is a pointer to an array, a length, and a capacity. Slices support
+the []
operator to access elements. The builtin
+len
function returns the
+length of the slice. The builtin cap
function returns the
+capacity.
+
+
+Given an array, or another slice, a new slice is created via
+a[I:J]
. This
+creates a new slice which refers to a
, starts at
+index I
, and ends at index
+J - 1
. It has length J - I
.
+If a
is itself a slice, the new slice refers to the same array
+to which a
+refers. That is, changes made using the new slice may be seen using
+a
. The
+capacity of the new slice is simply the capacity of a
minus
+I
. The capacity
+of an array is the length of the array. You may also assign a pointer to an
+array to a
+variable of slice type; given var s []int; var a[10] int
,
+s = &a
is more or
+less the same as s = a[0:len(a)]
.
+
+
+What this means is that Go uses slices for some cases where C++ uses pointers.
+If you create a value of type [100]byte
(an array of 100 bytes,
+perhaps a
+buffer) and you want to pass it to a function without copying it, you should
+declare the function parameter to have type []byte
, and pass the
+address
+of the array. Unlike C++, it is not
+necessary to pass the length of the buffer; it is efficiently accessible via
+len
.
+
+
+The slice syntax may also be used with a string. It returns a new string, +whose value is a substring of the original string. + +
Making values
+ +
+Go has a builtin function new
which takes a type and
+allocates space
+on the heap. The allocated space will be zero-initialized for the type.
+For example, new(int)
returns a new object of type
+*int
,
+allocated on the heap and initialized with the value 0
.
+Unlike C++, new
is a function, not an operator;
+new int
is a syntax error.
+
+
+Map and channel values must be allocated using the builtin function
+make
.
+A variable declared with map or channel type without an initializer will be
+automatically initialized to nil
.
+Calling make(map[int]int)
returns a newly allocated value of
+type map[int]int
.
+Note that make
returns a value, not a pointer. This is
+consistent with
+the fact that map and channel values are passed by reference. Calling
+make
with
+a map type takes an optional argument which is the expected capacity of the
+map. Calling make
with a channel type takes an optional
+argument which is the
+buffering capacity of the channel.
+
+
+The make
function may also be used to allocate a slice.
+In this case it
+allocates memory for the underlying array and returns a slice referring to it.
+There is one required argument, which is the number of elements in the slice.
+A second, optional, argument is the capacity of the slice. For example,
+make([]int, 10, 20)
. This is identical to
+new([20]int)[0:10]
. Since
+Go uses garbage collection, the newly allocated array will be discarded
+sometime after there are no references to the returned slice.
+
+
Interfaces
+ ++Where C++ provides classes and templates, Go provides interfaces. A +Go interface is similar to a C++ pure abstract class: a class with no +data members, with methods which are all pure virtual. However, in +Go, any type which provides the methods named in the interface may be +treated as an implementation of the interface. No explicitly declared +inheritance is required. The implementation of the interface is +entirely separate from the interface itself. + +
+A method looks like an ordinary function definition, except that it
+has a receiver. The receiver is similar to the this
pointer in a
+C++ class method.
+
+
+type my_type struct { i int } +func (p *my_type) get() int { return p.i } ++ +
+This declares a method get
associated with my_type
.
+The receiver is named p
in the body of the function.
+
+
+Given this interface: + +
+type my_interface interface { + get() int; + set(i int); +} ++ +
+we can make my_type
satisfy the interface by additionally writing
+
+
+func (p *my_type) set(i int) { p.i = i } ++ +
+Now any function which takes my_interface
as a parameter
+will accept a
+variable of type *my_type
.
+
+
+func get_and_set(x my_interface); +func f1() { + var p my_type; + get_and_set(&p); +} ++ +
+In other words, if we view my_interface
as a C++ pure abstract
+base
+class, defining set
and get
for
+*my_type
made *my_type
automatically
+inherit from my_interface
. A type may satisfy multiple interfaces.
+
+
+An anonymous field may be used to implement something much like a C++ child +class. + +
+type my_child_type struct { my_type; j int } +func (p *my_child_type) get() int { p.j++; return (&p.my_type).get() } ++ +
+This effectively implements my_child_type
as a child of
+my_type
.
+
+
+func f2() { + var p my_child_type; + get_and_set(&p) +} ++ +
+The set
method is effectively inherited from
+my_child_type
, because
+methods associated with the anonymous type are promoted to become methods
+of the enclosing type. In this case, because my_child_type
has an
+anonymous field of type my_type
, the methods of
+my_type
also become methods of my_child_type
.
+In this example, the get
method was
+overridden, and the set
method was inherited.
+
+
+This is not precisely the same as a child class in C++. When a parent +method is called, it receives a pointer to the field in the child class. +If the parent method calls some other method on its argument, it will call +the method associated with the parent class, not the method associated with +the child class. In other words, methods are not virtual functions. When +you want the equivalent of a virtual function, use an interface. + +
+A variable which has an interface type may be converted to have a
+different interface type. This conversion is implemented dynamically
+at runtime, like C++ dynamic_cast
. Unlike
+dynamic_cast
, there does
+not need to be any declared relationship between the two interfaces.
+
+
+type my_compare_interface interface { + print(); +} +func f3(x my_interface) { + x.(my_compare_interface).print() +} ++ +
+The conversion to my_compare_interface
is entirely dynamic.
+It will
+work as long as the underlying type of x (the "dynamic type") defines
+a print
method.
+
+
+Because the conversion is dynamic, it may be used to implement generic +programming similar to templates in C++. This is done by, e.g., +manipulating values of the minimal interface. + +
+type Any interface { } ++ +
+Containers may be written in terms of Any
, and the caller may cast
+the values back to the desired type. As the typing is dynamic rather
+than static, there is no equivalent of the way that a C++ template may
+inline the relevant operations. The operations are fully type-checked
+at runtime, but all operations will involve a function call.
+
+
+type iterator interface { + get() Any; + set(v Any); + increment(); + equal(arg *iterator) bool; +} ++ +
Processes
+ +
+Go permits starting a new process (a "goroutine") using the go
+statement. The go statement runs a function in a different process.
+All processes in a single program share the same address space.
+
+
+func server(i int) { for { print(i); sys.sleep(10) } } +go server(1); go server(2); ++ +
+(Note that the for
statement in the server
+function is equivalent to a C++ while (true)
loop).
+
+
+Processes are (intended to be) cheap. + +
+Function literals can be useful with the go
statement.
+
+
+var g int // global variable +go func(i int) { + s := 0 + for j := 0; j < i; j++ { s += j } + g = s +} (1000) // Passes argument 1000 to the function literal. ++ +
Channels
+ +
+Channels are used to communicate between processes. Any value may be
+sent over a channel. Channels are (intended to be) efficient and
+cheap. To send a value on a channel, use <-
as a binary
+operator. To
+receive a value on a channel, use <-
as a unary operator.
+When calling
+functions, channels are passed by reference.
+
+
+The Go library provides mutexes, but you can also use +a single process with a shared channel. +Here is an example of using a manager function to control access to a +single value. + +
+type cmd struct { get bool; val int } +func manager(ch chan cmd) { + var val int = 0; + for { + c := <- ch + if c.get { c.val = val; ch <- c } + else { val = c.val } + } +} ++ +
+In that example the same channel is used for input and output. This +means that if two processes try to retrieve the value at the same +time, the first process may read the response which was triggered by +the second process's request. In simple cases that is fine. For more +complex cases, pass in a channel. + +
+type cmd2 struct { get bool; val int; ch <- chan int; } +func manager2(ch chan cmd2) { + var val int = 0; + for { + c := <- ch + if c.get { c.ch <- val } + else { val = c.val } + } +} ++ +
+To use manager2, given a channel to it: + +
+func f4(ch <- chan cmd2) int { + my_ch := make(chan int); + c := cmd2 { true, 0, my_ch }; // Composite literal syntax. + ch <- c; + return <- my_ch; +} ++ +