惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
AWS News Blog
AWS News Blog
V
Vulnerabilities – Threatpost
D
Darknet – Hacking Tools, Hacker News & Cyber Security
量子位
博客园 - 叶小钗
AI
AI
T
Tor Project blog
Forbes - Security
Forbes - Security
W
WeLiveSecurity
博客园_首页
爱范儿
爱范儿
J
Java Code Geeks
B
Blog
G
GRAHAM CLULEY
aimingoo的专栏
aimingoo的专栏
Cloudbric
Cloudbric
C
CXSECURITY Database RSS Feed - CXSecurity.com
TaoSecurity Blog
TaoSecurity Blog
L
LINUX DO - 热门话题
阮一峰的网络日志
阮一峰的网络日志
有赞技术团队
有赞技术团队
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Simon Willison's Weblog
Simon Willison's Weblog
云风的 BLOG
云风的 BLOG
Google DeepMind News
Google DeepMind News
H
Help Net Security
博客园 - 三生石上(FineUI控件)
C
Cisco Blogs
C
Cybersecurity and Infrastructure Security Agency CISA
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
P
Palo Alto Networks Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Recent Commits to openclaw:main
Recent Commits to openclaw:main
博客园 - 司徒正美
The Last Watchdog
The Last Watchdog
Blog — PlanetScale
Blog — PlanetScale
T
The Blog of Author Tim Ferriss
S
Secure Thoughts
Spread Privacy
Spread Privacy
F
Fortinet All Blogs
月光博客
月光博客
大猫的无限游戏
大猫的无限游戏
S
SegmentFault 最新的问题
H
Hackread – Cybersecurity News, Data Breaches, AI and More
A
About on SuperTechFans
Security Latest
Security Latest
Webroot Blog
Webroot Blog
Scott Helme
Scott Helme
Hugging Face - Blog
Hugging Face - Blog

DigitalOcean Community Tutorials

Mastering grep with Regular Expressions for Efficient Text Search It's Time to Break Up with Your Cloud: Why AI Teams are Switching We Built a Private-Document AI App to Test Platform Security. Here Is What We Could Actually Verify. PostgreSQL Explained: A Complete Beginner-to-Advanced Guide How To Install and Configure Postfix on Ubuntu How To Build a Web Application Using Flask in Python 3 Build AI Reading List with DigitalOcean Functions and Mistral How To Concatenate Strings in Python How to Allow MySQL Remote Access Securely How To Install and Use Docker on Rocky Linux How To Build a Multi-Agent AI System with Docker Agent DSPy Use Cases: Build Optimized LLM Pipelines How To Submit AJAX Forms with jQuery Build an AI-Powered GPU Fleet Optimizer with the DigitalOcean AI Platform ADK Monitor GPU Utilization in Real Time: A Complete Guide Reduce File Size of Images in Linux - CLI and GUI methods Reduce PDF File Size in Linux: Tools and Methods How To Set Up a Private Docker Registry on Ubuntu How To Troubleshoot Terraform: Errors and Fixes How to Use Go Modules Python Multiprocessing Example: Process, Pool & Queue Convert Class Components to Functional Components with React Hooks How To Install and Configure Ansible on Ubuntu LLM Tokenizers Simplified: BPE, SentencePiece, and More How To Monitor System Authentication Logs on Ubuntu How to Use Traceroute and MTR to Diagnose Network Issues How to Deploy Postgres to Kubernetes Cluster Importing Packages in Go: A Complete Guide Create RAID Arrays with mdadm on Ubuntu How To Make an HTTP Server in Go How To Set Up Time Synchronization on Ubuntu How To Use Struct Tags in Go apt-key Deprecation: Add Repositories with GPG on Ubuntu Linux ps Command: 20 Real-World Examples Python struct.pack and struct.unpack for Binary Data Deadlock in Java: Examples, Detection, and Prevention How To Use Find and Locate to Search for Files on Linux Structured Resume Skill Extraction Using Mistral-7B Inference How to Use the Python Main Function How to Set Up NemoClaw on a DigitalOcean Droplet with 1-Click Build an End-to-End RAG Pipeline for LLM Applications From Single to Multi-Agent Systems: Key Infrastructure Needs Back Up Data to Object Storage Using Restic How to Generate Videos with LTX-2.3 on DigitalOcean GPU Droplets How To Install LAMP Stack (Apache, MySQL, PHP) on Ubuntu How to Download Files with cURL Generate UUIDs with uuidgen on Linux How To Use EJS to Template Your Node Application How to Install Node.js on Ubuntu (Step-by-Step Guide) MongoDB Indexes: Improve Query Performance with Node.js LLM Tool Calling with DigitalOcean AI Platform and Databases What are Text Diffusion Models? - An Overview Crafting a Game from Scratch with GPT-5.4 Building Long-Term Memory in AI Agents with LangGraph and Mem0 How To Install PHP 7.4 and Set Up a Local Development Environment on Ubuntu 20.04 Build a GraphQL API in Go to Upload Files to Spaces How To Lint and Format Code with ESLint in Visual Studio Code Train YOLO26 for Retail Object Detection on DigitalOcean GPUs How To Work with JSON in MySQL How to Use the JavaScript .map() Method Building a Scalable App with MongoDB Using DigitalOcean's MCP Server How to Create an SSH Key in Linux: Easy Step-by-Step Guide Measure MySQL Query Performance with mysqlslap How To Use *args and **kwargs in Python 3 Nemotron 3 helped me find the perfect dish rack? A2A vs MCP - How These AI Agent Protocols Actually Differ How To Install and Manage Supervisor Docker Container Images with Watchtower on Ubuntu Getting Started with Qwen3.5 Vision-Language Models How To Create a New Sudo-Enabled User on Ubuntu How to Use Ansible to Install and Set Up Docker on Ubuntu How To Enable Remote Desktop Protocol Using xrdp on Ubuntu 22.04 How To Convert a String to a List in Python How To Check If a String Contains Another String in Python How to Read a Properties File in Python Python Command Line Arguments: sys.argv, argparse, getopt Mastering Grep command in Linux/Unix: A Beginner's Tutorial Understanding Python Data Types How to Implement a Stack in C With Code Examples Python os.system() vs subprocess: Run System Commands How To Install and Use Docker Compose on Ubuntu How to Add and Delete Users on Ubuntu How To Order Query Results in Laravel Eloquent How To Define and Use Handlers in Ansible Playbooks How To Install and Use SQLite on Ubuntu How To Install and Use Homebrew on macOS How To Manage DateTime with Carbon in Laravel and PHP How To Install Git on Ubuntu How To Install and Secure Redis on Ubuntu How To Build and Install Go Programs on Linux Using ldflags to Set Version Information for Go Applications How To Build a Node.js Application with Docker How To Add JavaScript to HTML How To Reset Your MySQL or MariaDB Root Password How To Add Images in Markdown How To Set Up a Production Elasticsearch Cluster with Ansible How To Set Up a Firewall Using firewalld on CentOS Understanding Systemd Units and Unit Files How To Set Up Replication in MySQL How To Use the .htaccess File
How To Use Variadic Functions in Go
Gopher Guides · 2026-03-18 · via DigitalOcean Community Tutorials

Introduction

A variadic function is a function that accepts zero, one, or more values as a single argument. While variadic functions are not the common case, they can be used to make your code cleaner and more readable. By the end of this tutorial, you will be able to define variadic functions, pass slices with the spread operator, use the any type for type-flexible parameters, and choose between variadic and slice parameters in your own code.

Several functions in the Go standard library use variadic parameters, including fmt.Println, fmt.Printf, append, and log.Printf.

Key Takeaways

  • In a Go function signature, the variadic parameter uses the syntax ...T and must be the last parameter; the compiler enforces this at compile time.
  • Go compiles variadic arguments into a slice []T; when zero arguments are passed, that parameter is nil, not an empty slice.
  • The spread operator (...) after a slice at the call site unpacks the slice into discrete arguments; you cannot mix spread and discrete arguments in the same call.
  • Using ...any (or ...interface{}) allows a variadic function to accept values of mixed types at the cost of compile-time type safety; use type assertions inside the function when needed.
  • Prefer a variadic parameter when callers will pass discrete values at the call site; prefer a slice parameter when callers will pass an existing collection.
  • Calling a variadic function with no arguments is valid; the variadic parameter receives nil, so check with len(v) == 0 for “no arguments” and be aware that v == nil is true only when zero arguments were passed.
  • The built-in append function is variadic: you can pass discrete elements or spread a slice with append(s, others...).

Prerequisites

If you do not have a module initialized, run the following command in your working directory before running the examples:

  1. go mod init example.com/variadic

What Is a Variadic Function in Go

A variadic function is one that accepts zero, one, or more values for a single parameter. In the function signature, that parameter is written with an ellipsis before the type: ...T. For example, fmt.Println is declared as:

func Println(a ...any) (n int, err error)

The any type is an alias for interface{} introduced in Go 1.18. In older codebases you may still see ...interface{}; the behavior is the same.

A function with a parameter preceded by ... is a variadic function. Go compiles variadic arguments into a slice of that type. Inside the function, the parameter is a []T. When the caller passes zero arguments, Go does not allocate an empty slice; the parameter is nil. Code that checks len(names) == 0 behaves the same for both “no arguments” and “empty slice,” but names == nil is true only when zero arguments were passed. This distinction can matter when you pass a pre-existing slice with the spread operator.

The following program calls fmt.Println with zero, one, or more arguments:

print.go

package main

import "fmt"

func main() {
    fmt.Println()
    fmt.Println("one")
    fmt.Println("one", "two")
    fmt.Println("one", "two", "three")
}

Run it:

  1. go run print.go

Output

one one two one two three

The first line is blank because no arguments were passed. The rest show how multiple arguments are printed.

Defining a Variadic Function

Define a variadic function by putting an ellipsis (...) before the parameter type. The variadic parameter must be the last parameter in the function signature. Go allocates a backing array and wraps the supplied arguments into a slice before passing it to the function. When no arguments are passed for that parameter, the parameter is nil, not []string{}.

Example: a function that greets a list of names:

hello.go

package main

import "fmt"

func main() {
    sayHello()
    sayHello("Sammy")
    sayHello("Sammy", "Jessica", "Drew", "Jamie")
}

func sayHello(names ...string) {
    for _, n := range names {
        fmt.Printf("Hello %s\n", n)
    }
}

The parameter names is variadic (...string). Inside the function it is a slice of strings. The first call passes no arguments, so names is nil and the loop runs zero times.

  1. go run hello.go

Output

Hello Sammy Hello Sammy Hello Jessica Hello Drew Hello Jamie

Handling the zero-argument case explicitly:

hello.go

package main

import "fmt"

func main() {
    sayHello()
    sayHello("Sammy")
    sayHello("Sammy", "Jessica", "Drew", "Jamie")
}

func sayHello(names ...string) {
    if len(names) == 0 {
        fmt.Println("nobody to greet")
        return
    }
    for _, n := range names {
        fmt.Printf("Hello %s\n", n)
    }
}
  1. go run hello.go

Output

nobody to greet Hello Sammy Hello Sammy Hello Jessica Hello Drew Hello Jamie

Using a variadic parameter can improve readability at the call site. Compare a slice-based join with a variadic one. First, the version that takes a slice:

join.go

package main

import "fmt"

func main() {
    var line string

    line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"})
    fmt.Println(line)

    line = join(",", []string{"Sammy", "Jessica"})
    fmt.Println(line)

    line = join(",", []string{"Sammy"})
    fmt.Println(line)
}

// Slice parameter: caller must wrap values in a []string literal
func join(del string, values []string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}
  1. go run join.go

Output

Sammy,Jessica,Drew,Jamie Sammy,Jessica Sammy

Callers must wrap every list in a slice. The variadic version allows discrete arguments:

join.go

package main

import "fmt"

func main() {
    var line string

    line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
    fmt.Println(line)

    line = join(",", "Sammy", "Jessica")
    fmt.Println(line)

    line = join(",", "Sammy")
    fmt.Println(line)
}

// Variadic parameter: caller passes discrete string arguments
func join(del string, values ...string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}
  1. go run join.go

Output

Sammy,Jessica,Drew,Jamie Sammy,Jessica Sammy

Both versions behave the same; the variadic function signature makes the call site cleaner when you have discrete values.

Variadic Argument Order

A function may have only one variadic parameter, and it must be the last parameter. Any other order causes a compile error. This rule is enforced at compile time; there is no way to work around it with reflection or type tricks.

Example that does not compile:

join_bad.go

package main

import "fmt"

func main() {
    line := join(",", "Sammy", "Jessica")
    fmt.Println(line)
}

// Error: variadic parameter is not last — this does not compile
func join(values ...string, del string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}
  1. go run join_bad.go

Output

./join_bad.go:11:28: syntax error: cannot use ... with non-final parameter values

When defining a variadic function, only the last parameter can be variadic.

A variadic parameter must always be the last parameter in a function signature. Placing it in any other position causes a compilation error: syntax error: cannot use ... with non-final parameter

Using the Spread Operator to Pass a Slice

You can pass zero, one, or more arguments to a variadic function. When you already have a slice, you must unpack it at the call site with the spread operator (...); otherwise the compiler expects discrete arguments of type T, not []T.

Without the spread operator, passing a slice is a type error:

join_no_spread.go

package main

import "fmt"

func main() {
    names := []string{"Sammy", "Jessica", "Drew", "Jamie"}
    line := join(",", names)
    fmt.Println(line)
}

// Error: passing []string directly to a variadic parameter — this does not compile
func join(del string, values ...string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}
  1. go run join_no_spread.go

Output

./join_no_spread.go:10:14: cannot use names (type []string) as type string in argument to join

Suffix the slice with ... to turn it into discrete arguments:

join.go

package main

import "fmt"

func main() {
    names := []string{"Sammy", "Jessica", "Drew", "Jamie"}
    line := join(",", names...)
    fmt.Println(line)
}

// Correct: spread operator unpacks the slice into discrete arguments
func join(del string, values ...string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}
  1. go run join.go

Output

Sammy,Jessica,Drew,Jamie

Passing a nil slice with the spread operator: When you spread a nil slice, the variadic parameter receives nil, not an empty slice. Both result in len(values) == 0, but the parameter will be nil:

spread_nil.go

package main

import "fmt"

func main() {
    var names []string
    show(names...)
}

func show(v ...string) {
    fmt.Printf("len=%d, isNil=%v\n", len(v), v == nil)
}
  1. go run spread_nil.go

Output

len=0, isNil=true

So when zero arguments are passed (or a nil slice is spread), the variadic parameter is nil.

You cannot mix spread and discrete arguments. The spread must be the only source of variadic arguments in that call. The following does not compile:

join_mix.go

package main

import "fmt"

func main() {
    names := []string{"Sammy", "Jessica"}
    line := join(",", names..., "extra")
    fmt.Println(line)
}

func join(del string, values ...string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}
  1. go run join_mix.go

Output

./join_mix.go:10:28: syntax error: cannot use ... with trailing arguments

You can combine different call styles across different calls: discrete args in one call, spread in another:

join.go

package main

import "fmt"

func main() {
    line := join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"}...)
    fmt.Println(line)

    line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
    fmt.Println(line)

    line = join(",", "Sammy", "Jessica")
    fmt.Println(line)

    line = join(",", "Sammy")
    fmt.Println(line)
}

// All valid call styles: spread literal, discrete args, and single arg
func join(del string, values ...string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}
  1. go run join.go

Output

Sammy,Jessica,Drew,Jamie Sammy,Jessica,Drew,Jamie Sammy,Jessica Sammy

Variadic Functions with the any Type

When you need a variadic function to accept values of different types, use ...any (or ...interface{} in older code). The compiler will accept any mix of types; inside the function you treat each value as any and use type assertions or type switches when you need to operate on concrete types. This trades compile-time type safety for flexibility, as with interfaces in Go.

Example: a simple logger that prints each value with its type:

log_values.go

package main

import "fmt"

func main() {
    logValues("user", "alice", 42, true)
    logValues("config", "env", "prod", 8080)
}

func logValues(prefix string, values ...any) {
    fmt.Printf("[%s] ", prefix)
    for _, v := range values {
        fmt.Printf("%T: %v; ", v, v)
    }
    fmt.Println()
}
  1. go run log_values.go

Output

[user] string: alice; int: 42; bool: true; [config] string: env; string: prod; int: 8080;

When you need to act on the concrete type of each value, use a type switch:

type_switch.go

package main

import "fmt"

func main() {
    describe("count", 42, "hello", true, 3.14)
}

func describe(label string, values ...any) {
    fmt.Printf("[%s]\n", label)
    for _, v := range values {
        switch val := v.(type) {
        case string:
            fmt.Printf("  string: %q\n", val)
        case int:
            fmt.Printf("  int: %d\n", val)
        case bool:
            fmt.Printf("  bool: %v\n", val)
        default:
            fmt.Printf("  other (%T): %v\n", val, val)
        }
    }
}
  1. go run type_switch.go

Output

[count] int: 42 string: "hello" bool: true other (float64): 3.14

The default case handles any type you have not explicitly listed. Use this pattern any time you need different behavior per type rather than just printing the value.

When you operate on individual values inside the function, you must use type assertions or type switches to get concrete types. Using any (or interface{}) gives flexibility but removes compile-time type safety. For more on types and interfaces, see How to Use Interfaces in Go.

How the append Function Uses Variadic Arguments

The built-in append is a variadic function. Its signature is:

func append(slice []Type, elems ...Type) []Type

You can append discrete elements or spread another slice. Both patterns are valid:

Pattern (a): append discrete elements

append_discrete.go

package main

import "fmt"

func main() {
    s := []int{10, 20}
    s = append(s, 1, 2, 3)
    fmt.Println("discrete:", s)
}

Pattern (b): append a slice with spread

append_spread.go

package main

import "fmt"

func main() {
    s := []int{10, 20}
    others := []int{1, 2, 3}
    s = append(s, others...)
    fmt.Println("spread:", s)
}

Combined in one runnable example:

append_combined.go

package main

import "fmt"

func main() {
    s := []int{10, 20}
    s = append(s, 1, 2, 3)
    fmt.Println("discrete:", s)

    s = []int{10, 20}
    others := []int{1, 2, 3}
    s = append(s, others...)
    fmt.Println("spread:", s)
}
  1. go run append_combined.go

Output

discrete: [10 20 1 2 3] spread: [10 20 1 2 3]

Understanding variadic internals makes append predictable: when you spread a nil slice, no extra elements are appended, and the variadic parameter inside append receives nil.

Variadic Functions vs Slice Parameters

Use a variadic parameter when callers pass discrete values at the call site. Use a slice parameter when callers already have a collection and will pass it directly, for example when the slice comes from another function’s return value or from unmarshaling JSON.

Aspect Variadic (...T) Slice ([]T)
Call-site syntax Discrete values or one spread slice: f(1, 2, 3) or f(s...) Single slice: f(s)
Zero-value behavior No args: parameter is nil. Spread nil slice: parameter is nil. Caller can pass nil or empty slice; both are explicit.
Spread existing slice Yes: f(s...) N/A; slice is the argument.
Compile-time argument count Flexible; zero or more. Fixed: one slice argument.
Idiomatic use Callers pass a few discrete values or one slice they unpack. Callers already have a collection (e.g., from another API).

Practical Patterns and Real-World Examples

Example (a): Safe zero-argument sum

A variadic sum that returns 0 when no arguments are passed:

sum.go

package main

import "fmt"

func main() {
    fmt.Println("sum() =", sum())
    fmt.Println("sum(1,2,3) =", sum(1, 2, 3))
}

func sum(n ...int) int {
    total := 0
    for _, v := range n {
        total += v
    }
    return total
}
  1. go run sum.go

Output

sum() = 0 sum(1,2,3) = 6

Example (b): buildQuery for a SQL WHERE clause

Construct a WHERE clause by joining filter strings (e.g., for building queries). Zero filters returns the base; multiple filters are joined with AND:

build_query.go

package main

import "fmt"

func main() {
    fmt.Println(buildQuery("SELECT * FROM users"))
    fmt.Println(buildQuery("SELECT * FROM users", "active = true"))
    fmt.Println(buildQuery("SELECT * FROM users", "active = true", "role = 'admin'"))
}

func buildQuery(base string, filters ...string) string {
    if len(filters) == 0 {
        return base
    }
    q := base + " WHERE "
    for i, f := range filters {
        if i > 0 {
            q += " AND "
        }
        q += f
    }
    return q
}
  1. go run build_query.go

Output

SELECT * FROM users SELECT * FROM users WHERE active = true SELECT * FROM users WHERE active = true AND role = 'admin'

Frequently Asked Questions About Variadic Functions in Go

Q1: What is a variadic function in Go?

A variadic function accepts zero, one, or more values for a single parameter. The parameter is declared with ...T and is implemented as a slice []T inside the function.

Q2: Can a Go function have more than one variadic parameter?

No. A function may have only one variadic parameter, and it must be the last parameter. The compiler reports an error otherwise.

Q3: What is the difference between passing a slice and using the spread operator?

Passing a slice s as f(s) passes one argument of type []T. Using the spread operator f(s...) unpacks the slice into individual T arguments, so the variadic parameter receives those elements. You cannot mix spread and other arguments in the same call.

Q4: What happens when you call a variadic function with no arguments?

The variadic parameter is nil. Its length is 0. Use len(p) == 0 to detect “no arguments”; p == nil is true only when zero arguments were passed (or a nil slice was spread).

Q5: Can you use variadic functions with generics in Go 1.18+?

Yes. You can define a generic variadic function, e.g. func F[T any](v ...T). The variadic parameter is still the last parameter and is implemented as a slice of the type parameter.

Q6: Why does fmt.Println use a variadic parameter?

fmt.Println must accept an arbitrary number of values of any type to print. A variadic parameter of type ...any allows multiple arguments of mixed types in a single call without the caller wrapping them in a slice.

Q7: Is there a performance difference between variadic functions and slice parameters?

In practice the difference is negligible. The compiler turns variadic arguments into a slice; passing a slice directly avoids that step but both are cheap. Choose based on call-site clarity and API design, not micro-optimization.

Q8: Can you append to the variadic parameter slice inside the function?

Yes. The parameter is a slice and you can append to it inside the function. The append affects only the local copy of the slice header; the caller’s original arguments are not changed. You can verify this:

append_local.go

package main

import "fmt"

func main() {
    names := []string{"Sammy", "Jessica"}
    addName(names...)
    fmt.Println("caller slice after call:", names)
}

func addName(names ...string) {
    names = append(names, "Drew")
    fmt.Println("inside function:", names)
}
  1. go run append_local.go

Output

inside function: [Sammy Jessica Drew] caller slice after call: [Sammy Jessica]

The caller’s slice is unchanged because append inside the function operates on the local slice header. The only exception is if the backing array has enough capacity to absorb the append without reallocation. In that case the new element is written into the shared backing array, but the caller’s slice header still has the original length and will not see the new element unless you explicitly return the modified slice.

Conclusion

Variadic functions are the right tool when callers naturally pass a variable number of discrete values or when you want a single slice to be unpacked at the call site. Use the spread operator to pass an existing slice, and remember that zero arguments (or spreading a nil slice) make the variadic parameter nil. For mixed types, use ...any and handle type assertions inside the function; for a single type with a variable count, ...T keeps the API clear.

To go further, see How to Define and Call Functions in Go, Understanding Arrays and Slices in Go, Understanding Data Types in Go, How to Use Interfaces in Go, and How to Write Packages in Go.

Creative CommonsThis work is licensed under a Creative Commons Attribution-NonCommercial- ShareAlike 4.0 International License.