Skip to main content
Redhat Developers  Logo
  • Products

    Featured

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat OpenShift AI
      Red Hat OpenShift AI
    • Red Hat Enterprise Linux AI
      Linux icon inside of a brain
    • Image mode for Red Hat Enterprise Linux
      RHEL image mode
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • Red Hat Developer Hub
      Developer Hub
    • View All Red Hat Products
    • Linux

      • Red Hat Enterprise Linux
      • Image mode for Red Hat Enterprise Linux
      • Red Hat Universal Base Images (UBI)
    • Java runtimes & frameworks

      • JBoss Enterprise Application Platform
      • Red Hat build of OpenJDK
    • Kubernetes

      • Red Hat OpenShift
      • Microsoft Azure Red Hat OpenShift
      • Red Hat OpenShift Virtualization
      • Red Hat OpenShift Lightspeed
    • Integration & App Connectivity

      • Red Hat Build of Apache Camel
      • Red Hat Service Interconnect
      • Red Hat Connectivity Link
    • AI/ML

      • Red Hat OpenShift AI
      • Red Hat Enterprise Linux AI
    • Automation

      • Red Hat Ansible Automation Platform
      • Red Hat Ansible Lightspeed
    • Developer tools

      • Red Hat Trusted Software Supply Chain
      • Podman Desktop
      • Red Hat OpenShift Dev Spaces
    • Developer Sandbox

      Developer Sandbox
      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Secure Development & Architectures

      • Security
      • Secure coding
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
      • View All Technologies
    • Start exploring in the Developer Sandbox for free

      sandbox graphic
      Try Red Hat's products and technologies without setup or configuration.
    • Try at no cost
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • Java
      Java icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • API Catalog
    • Product Documentation
    • Legacy Documentation
    • Red Hat Learning

      Learning image
      Boost your technical skills to expert-level with the help of interactive lessons offered by various Red Hat Learning programs.
    • Explore Red Hat Learning
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

Go for C++ developers: A beginner's guide

August 30, 2023
Stan Cox
Related topics:
Programming languages & frameworksC, C#, C++Go
Related products:
Red Hat Enterprise Linux

Share:

    After years of working on software written in C and C++, I switched to working on a project that is implemented in Go. More developers may find themselves working in the Go ecosystem as more software, such as Red Hat OpenShift and Kubernetes, is implemented in Go. This article discusses the primary language differences between Go and C++, differences in the development environments, and differences in the program-building environment. Examples and code snippets are from the Grafana sources.

    Program syntax

    Go statements are terminated by a semicolon. Go treats the end of a non-blank line as a semicolon unless it can be determined that the line is incomplete. As a result, go requires the opening brace to be on the same line as the function definition:

     func (s *Server) init() error {
      ...
     }
    }

    Go requires the closing then brace to be on the same line as the else:

    if hs.Cfg.UnifiedAlerting.IsEnabled() {
    	notifiersAuthHandler = reqSignedIn
    } else {
    	notifiersAuthHandler = reqEditorRole
    }

    Variable and type declaration

    Go declarations start with the keyword var followed by the name and then the type, which is the opposite of C. A series of declarations may be placed in parentheses.

     var dashboardId int64
     var (
             hasPublicDashboard bool
             err                error
     )
    // Go uses the clearer form:
     var r1, r2 *regexp.Regexp
    // whereas the C equivalent would be:
     regexp.Regexp *r1, *r2;

    A variable may be initialized when it is declared. If the type is not specified, the variable type will be the type of the initialization expression. var msg = "unknown" which will define msg as a string.

    A short declaration syntax is allowed within a function or loop. One or more variables may be defined this way. If multiple variables are defined using this method, then one of the variables may have already been declared. This is often used to define and use an error variable.

    req, err := http.NewRequest("GET", url, nil)
    ...
    resp, err := client.Do(req)

    A type declaration defines a new named type. A common use is to create a type for a defined structure.

    type Leaf struct {
         ...
    }
    type patternType int8 
    leaves   []*Leaf
    typ        patternType

    Variable assignment

    Go has a blank identifier, which causes the return value of the right side of the assignment to be ignored.

    _ = l.next()

    Go permits multiple assignments, which are done in parallel.

    i, j = j, i    // Swap i and j.

    Any declared but not explicitly initialized variable is automatically initialized to the zero value of the type: false for booleans, 0 for numeric types,"" for strings, and nil for other types. A variable must either be initialized or the type must be specified.

    Go, unlike C++, requires that two variables can only be compared or assigned if their type definitions match. Go does not support implicit type conversion.

    Go constants can be typed or untyped. If the type is present, then the expressions must be assignable to that type. If the type is omitted, the constant takes the type of the expression. An untyped numeric constant represent values using arbitrary precision.

    const eof = -1		// untyped integer
    const orgID int64 = 2  // int64 

    Data containers

    Go does not support enums. Instead, you can use the special name iota in a constant declaration that represents successive untyped integer constants.

    type State int
    const (
    	Normal State = iota
    	Alerting
    	Pending
    	NoData
    	Error
    )

    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; slices hold pointers to underlying arrays.

    A slice can be understood to be a struct with three fields: an array pointer, a length, and a maximum size. Slices use the [] operator to access elements of the underlying array. The len function returns the length of the slice, and the cap function returns the maximum size.

    name = name[lastslash+1:]
    encrypted := []byte{0x2a, 0x59, 0x57, 0x56, 0x78}

    Given an array or another slice, a new slice is created via:

    newarr := arr[i:j]

    where newarr starts at index i and ends prior to index j. newarr refers to arr, thus changes made to newarr are reflected in arr. An array pointer can be assigned to a variable of slice type:

    var s []int ; var a[10] int ; s = &a

    A slice is similar to std::vector in C++.

    Hash tables are provided by the language. They are called maps. A map is an unordered collection of key-value pairs where the keys are unique. It is written as map[key_type]value_type,where key_type is the type of the map key and value_type is the type of the map value.

    encrypted := make(map[string][]byte)
    encrypted[key] = encryptedData

    A Go map is similar to std::unordered_map in C++.

    Strings are provided by the language. They are immutable and cannot be changed once they have been created.

    Expressions

    The for statement is the only looping construct in Go. It may be used with a single condition, which is equivalent to a while statement, or the condition can be omitted, which is an endless loop. Parenthesis are not required:

    for i := 1; i <= int(orgID); i++ {
    	...         
    }

    A for statement can also iterate through the entries of an array, slice, string, or map:

    for _, team := range query.Result {
        ...
    }

    The blank identifier _ is an anonymous placeholder which indicates that the first value returned by range, the index, is ignored. Go permits break and continue to specify a label. The label must refer to a for, switch, or select statement.

    LOOP:
           for {
    	       ...
    	       break LOOP
           }

    In a switch statement, case labels do not fall through. You can make them fall through using the fallthrough keyword. A case may have multiple values. The case value need not be an integer; it can be any type that supports equality comparisons.

    switch hs.Cfg.Protocol {
    case setting.HTTPScheme, setting.SocketScheme:
    ...
    default:
    }

    The increment and decrement operators may only be used in statements, not in expressions.

    patchedIndex++

    Go has pointers but not pointer arithmetic. You cannot use a pointer variable to walk through the bytes of a string. Go uses nil for invalid pointers, where C++ uses NULL or simply 0.

    b := &bytes.Buffer{}
    copy := *msg

    Functions

    A function in Go is defined with the func keyword. Input and output parameters are defined separately. There can be multiple return types. In the following example, name is an input string parameter. The function returns two strings.

    func splitName(name string) (string, string) {
    	names := util.SplitString(name)
    	switch len(names) {
    	case 0:
    		return"  ",""
    	case 1:
    		return names[0], ""
    	default:
    		return names[0], names[1]
    	}
    }

    The return values can be returned by name. For example, in the above:

    func splitName(name string) (s1 string, s2 string) {
           ...
           s1 = names[0]
           s2 = names[1]
           ...
    }

    Parameters are passed by value except for maps and slices, which are passed by reference. The defer statement can be used to call a function after the function containing the defer statement returns.

    func performGet(url string, av *Avatar, handler ResponseHandler) error {
         ...
         defer func() {
    	      if err := resp.Body.Close(); err != nil {
    	       alog.Warn("Failed to close response body", "err", err)
         }()
         ...
    }

    This is typically used to handle cleanup, such as closing files before the containing function returns. A function definition without a corresponding function name is an anonymous function, as shown in the previous example. The anonymous function can reference variables in the containing function's scope.

    Each variable in Go exists as long as there are references to the variable. Go uses garbage collection to free the variable's memory when there are no longer references to it. The memory cannot be released explicitly. The garbage collection is intended to be incremental and efficient on modern processors.

    Interfaces

    Go uses interfaces in situations where C++ uses classes, subclasses, and templates. A Go interface is similar to a C++ pure abstract class: a class with pure virtual methods and no data members. Go allows any type that provides the methods named in the interface to be treated as an implementation of the interface.

    type Expander interface {
            SetupExpander(file *ini.File) error
            Expand(string) (string, error)
    }

    A method definition is similar to a function definition with the addition of a receiver, which is similar to the this pointer in a C++ class method.

    type fileExpander struct {
    }
    
    func (e fileExpander) SetupExpander(file *ini.File) error {
           return nil
    }
    func (e fileExpander) Expand(s string) (string, error) {
    ...
    }

    fileExpander implements the Expander interface by defining a SetupExpander and Expand method. Any function that takes Expander as a parameter will accept a variable of type fileExpander. If we think of fileExpander as a C++ pure abstract base class, then defining SetupExpander and Expand for fileExpander made it inherit from Expander. A type may satisfy multiple interfaces.

    An anonymous field can be used to implement something resembling a C++ child class:

            type myExpanderType struct { Expander; count int }

    This implements myExpanderType as a child of Expander that inherits its methods.

    A variable that has an interface type may be converted to have a different interface type using a special construct called a type assertion. This is implemented dynamically at run time, like C++ dynamic cast. Unlike dynamic cast, there does not need to be any declared relationship between the two interfaces. This is typically used in a type switch, which switches based on the type of value:

            switch v := value.(type) {
            case time.Time:
            	return v.Format(timeFormat)
            case error:
            	return v.Error()
            case fmt.Stringer:
            	return v.String()
            default:
            	return v
            }

    Goroutines

    Go permits starting a new thread of execution, known as a goroutine, using the go statement. The function runs in a different, newly created goroutine, which shares the address space with the parent. The Go runtime schedules an arbitrary number of goroutines onto a random number of OS threads. Goroutines are more lightweight than OS threads, use a smaller stack, and are scheduled by a userspace runtime scheduler.

              func updateUsageStats(ctx context.Context, reader *bluge.Reader, logger log.Logger) {
                   ...
                   }
              go updateUsageStats(context.Background(), reader, i.logger)

    Go statements frequently use function literals:

                   go func() {
                            defer close(done)
                            for {
                            ...
                   }()

    Channels

    Channels are used to communicate between goroutines. Any value may be sent over a channel. Channels are 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. A channel can control access to a single value.

            attemptChan := make(chan int, 1)
            attemptChan <- 1
            for {
               ...
               go e.processJob(attemptID, attemptChan, cancelChan, job)
               select {
            	   case <-unfinishedWorkTimer.C:
            	 return e.endJob(grafanaCtx.Err(), cancelChan, job)
                  case <-attemptChan:
            	 return e.endJob(nil, cancelChan, job)
                  }

    Development environment

    Go provides dependency management, interface abstraction, and documentation tools to assist with programming in the large.

    The go command provides various tools for managing the Go development environment:

    • go help gives an overview of the go command. 
    • go env displays the environment variables that define the Go environment.
    • The Go compiler and tools are installed into the value displayed by GOROOT.
    • gofmt is a tool that enforces layout rules. Most Go code has been run through gofmt. It enforces a single standard Go style.

    Go does not use header files. Instead, each source file is a member of a set of related files that form a module. Go has tools to manage versions of modules. Executables created by go are typically statically linked; thus the referenced modules are statically linked.

    When a package defines an object (type, constant, variable, function) with a name starting with an upper case letter, that object is visible to any other file that imports that package. Exported names and functions form a module application binary interface, which should not be changed or removed within a major release so that compatibility is maintained.

    The environment variable GOPATH, which is typically $HOME/go and can be displayed by go env, contains a directory: pkg where compiled package files are stored based on import statements. Go downloads modules into the directory $GOMODCACHE; the default path is pkg/mod.

    go mod init creates a new go.mod file, which defines the module contents in the current directory. If after creating go.mod we create a simple Go program:

    package main
    
    import (
    "fmt"
    )
    
    func main() {
    var s string = "abc"
    ss := "def"
    var ssa = []string {"efg","hij"}
    fmt.Println(s, ss, ssa)
    }

    go mod init example.com/test creates a go.mod that describes a module's characteristics. 

    go mod edit modifies the attributes in go.mod. 

    go mod tidy will download any modules that our module is importing.

    go run . will build and execute a Go program. 

    go build . will build a Go program

    ./test abc def [efg hij]

    Go provides a test infrastructure. To add a module test, create a program MOD_test.go, where MOD is the module name. The program imports "testing." Each test routine is called Test where Testfoo is an individual test name. t.Fatalf is called if the test fails.

    package MOD
    import {
        "testing"
    }
     func Testfoo(t *testing.T) {
         ...
         if ... {
            t.Fatal(`Expected %d but got %d, error`, expected, value)
         }
    }     
    

    Conclusion

    Go is an easy-to-learn language with a large ecosystem of tools. This article gives a C++ programmer the fundamentals to start using Go effectively.

    Related Posts

    • Using Delve to debug Go programs on Red Hat Enterprise Linux

    • Build a Go application using OpenShift Pipelines

    • Go and FIPS 140-2 on Red Hat Enterprise Linux

    • Create a Kubernetes Operator in Golang to automatically manage a simple, stateful application

    • Managing stateful applications with Kubernetes Operators in Golang

    Recent Posts

    • How Trilio secures OpenShift virtual machines and containers

    • How to implement observability with Node.js and Llama Stack

    • How to encrypt RHEL images for Azure confidential VMs

    • How to manage RHEL virtual machines with Podman Desktop

    • Speech-to-text with Whisper and Red Hat AI Inference Server

    What’s up next?

    Download Java in a Nutshell, the reference guide every Java developer needs at their fingertips. It’s designed to help you get the most out of versions through Java 17, with examples that show how to take advantage of modern Java APIs and development best practices.

    Get the e-book
    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Products

    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform

    Build

    • Developer Sandbox
    • Developer Tools
    • Interactive Tutorials
    • API Catalog

    Quicklinks

    • Learning Resources
    • E-books
    • Cheat Sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site Status Dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue