Question
I can declare a constant string like this:
const ascii = "abcdefghijklmnopqrstuvwxyz"
But these attempts to declare a constant collection of floating-point values do not work:
const letter_goodness []float32 = { .0817, .0149, .0278, .0425, .1270, .0223, .0202, .0609, .0697, .0015, .0077, .0402, .0241, .0675, .0751, .0193, .0009, .0599, .0633, .0906, .0276, .0098, .0236, .0015, .0197, .0007 }
const letter_goodness = { .0817, .0149, .0278, .0425, .1270, .0223, .0202, .0609, .0697, .0015, .0077, .0402, .0241, .0675, .0751, .0193, .0009, .0599, .0633, .0906, .0276, .0098, .0236, .0015, .0197, .0007 }
const letter_goodness = []float32{ .0817, .0149, .0278, .0425, .1270, .0223, .0202, .0609, .0697, .0015, .0077, .0402, .0241, .0675, .0751, .0193, .0009, .0599, .0633, .0906, .0276, .0098, .0236, .0015, .0197, .0007 }
The string constant works, but the array or slice versions do not. How do you correctly declare and initialize a constant array of floats in Go?
Short Answer
By the end of this page, you will understand why Go allows const for simple compile-time values like strings and numbers, but not for arrays or slices. You will also learn the correct way to store fixed float data in Go using variables, arrays, slices, and patterns that make the data effectively read-only.
Concept
In Go, const is only for constant values known at compile time. That includes values such as:
- numbers
- booleans
- strings
- rune constants
It does not include:
- arrays
- slices
- maps
- structs
That is why this works:
const ascii = "abcdefghijklmnopqrstuvwxyz"
A string can be a constant in Go.
But this does not work:
const letterGoodness = []float32{0.0817, 0.0149}
A slice is not a constant type. It is a runtime data structure containing a pointer, length, and capacity.
Similarly, arrays are also not allowed as constants in Go. Even though an array has fixed size, Go still does not permit arrays in const declarations.
So if you want fixed float data, the usual solution is to use a package-level variable:
var letterGoodness = []float32{0.0817, 0.0149, 0.0278}
or a package-level array:
var letterGoodness = [...]float32{0.0817, 0.0149, 0.0278}
This matters in real programs because many values feel “constant” from a design point of view, but Go only treats a narrow set of values as true language constants. For collections, you usually model immutability by convention: initialize once, then do not modify them.
Mental Model
Think of Go const values as values printed directly into the program at compile time.
- A number constant is like writing a number on paper.
- A string constant is like printing a fixed label.
- A slice is more like a small container object that points to data somewhere else.
Go allows const for simple printed values, but not for containers.
So:
const "hello"works because it is a simple fixed value.const []float32{...}does not work because a slice is a runtime structure.
If you need a fixed collection, use a variable and treat it as read-only.
Syntax and Examples
The correct syntax depends on whether you want an array or a slice.
1. Package-level array
Use an array if the size is fixed and known.
var letterGoodness = [...]float32{
0.0817, 0.0149, 0.0278, 0.0425, 0.1270, 0.0223, 0.0202,
0.0609, 0.0697, 0.0015, 0.0077, 0.0402, 0.0241, 0.0675,
0.0751, 0.0193, 0.0009, 0.0599, 0.0633, 0.0906, 0.0276,
0.0098, 0.0236, 0.0015, 0.0197, 0.0007,
}
[...]float32 tells Go to count the number of elements automatically.
2. Package-level slice
Use a slice if you want slice behavior.
var letterGoodness = []float32{
0.0817, 0.0149, 0.0278, 0.0425, 0.1270, 0.0223, 0.0202,
0.0609, 0.0697, 0.0015, 0.0077, 0.0402, 0.0241, 0.0675,
0.0751, 0.0193, 0.0009, 0.0599, 0.0633, 0.0906, 0.0276,
0.0098, 0.0236, 0.0015, 0.0197, 0.0007,
}
3. Return a copy to avoid accidental mutation
If you do not want callers to modify shared data, return a copy.
var letterGoodnessData = [...]float32{
0.0817, 0.0149, 0.0278,
}
func LetterGoodness() []float32 {
copyData := make([]float32, len(letterGoodnessData))
copy(copyData, letterGoodnessData[:])
return copyData
}
This is a common way to make collection data effectively read-only.
Beginner example
package main
import "fmt"
var scores = [...]float32{1.2, 3.4, 5.6}
func main() {
fmt.Println(scores)
}
This prints the array. It is not a const, but it stores fixed data successfully.
Step by Step Execution
Consider this example:
package main
import "fmt"
var weights = [...]float32{0.5, 1.5, 2.5}
func main() {
fmt.Println(weights)
fmt.Println(weights[1])
}
Here is what happens step by step:
- Go sees
var weights = [...]float32{0.5, 1.5, 2.5}. varmeans this is a variable, not a constant.[...]float32means:- create an array
- element type is
float32 - count the length from the values provided
- Go creates an array of length 3.
- The values are stored in order:
- index
0→0.5 - index
1→1.5 - index
2→2.5
- index
- In
main,fmt.Println(weights)prints the full array. fmt.Println(weights[1])prints the element at index1, which is1.5.
If you tried the same thing with const, compilation would fail before the program ran, because arrays and slices are not valid constant types in Go.
Real World Use Cases
Even though Go does not support constant arrays or slices, fixed collections are common in real programs.
Common uses
- Lookup tables: letter frequencies, scoring tables, conversion factors
- Configuration defaults: default ports, retry delays, allowed file extensions
- Static reference data: month names, country codes, status labels
- Parsing and validation: known token lists, keyword lists, accepted values
- Data science or analysis scripts: weight values, coefficients, sample distributions
Example: letter frequency data
var englishLetterFrequency = [...]float32{
0.0817, 0.0149, 0.0278, 0.0425, 0.1270, 0.0223, 0.0202,
0.0609, 0.0697, 0.0015, 0.0077, 0.0402, 0.0241, 0.0675,
0.0751, 0.0193, 0.0009, 0.0599, 0.0633, 0.0906, 0.0276,
0.0098, 0.0236, 0.0015, 0.0197, 0.0007,
}
This is ideal for algorithms that score text, detect language, or analyze letter usage.
Real Codebase Usage
In real Go codebases, developers usually handle “constant-like collections” with one of these patterns.
1. Package-level unexported variable
var defaultTimeouts = []int{1, 2, 5}
This works when only the package uses the data.
2. Unexported data with exported accessor
var defaultTimeouts = [...]int{1, 2, 5}
func DefaultTimeouts() []int {
out := make([]int, len(defaultTimeouts))
copy(out, defaultTimeouts[:])
return out
}
This prevents callers from modifying the original shared data.
3. Guarding against mutation
If shared data must not change, avoid returning the original slice directly.
Broken pattern:
var defaults = []int{1, 2, 3}
func GetDefaults() []int {
return defaults
}
Any caller can change defaults.
Safer pattern:
func GetDefaults() []int {
out := make([]int, len(defaults))
copy(out, defaults)
return out
}
4. Arrays for fixed-size data
Arrays are useful when the number of items should never change.
var rgb = [3]uint8{255, 128, 0}
This communicates fixed size more clearly than a slice.
5. Constants for individual values
Developers often keep simple values as constants and collections as variables.
const alphabet = "abcdefghijklmnopqrstuvwxyz"
var frequencies = [...]float32{0.0817, 0.0149}
Common Mistakes
1. Trying to use const with a slice
Broken code:
const values = []float32{1.1, 2.2}
Why it fails:
- slices are not valid constant types in Go
Use this instead:
var values = []float32{1.1, 2.2}
2. Trying to use const with an array
Broken code:
const values = [2]float32{1.1, 2.2}
Why it fails:
- arrays also cannot be declared as constants in Go
Use this instead:
var values = [2]float32{1.1, 2.2}
3. Forgetting the type in a composite literal
Broken code:
var values = {1.1, 2.2}
Why it fails:
- Go needs the array or slice type before the values
Correct version:
var values = []float32{1.1, 2.2}
4. Assuming var means unsafe to share
A package-level var is fine for fixed reference data, but remember it can be modified.
Avoid accidental changes by:
- keeping it unexported
- returning copies
- documenting it as read-only
5. Mixing arrays and slices
These are different types:
var a = [3]float32{1, 2, 3}
var s = []float32{1, 2, 3}
- array: fixed size, part of the type
- slice: flexible view over an underlying array
Comparisons
| Concept | Example | Can use const? | Size fixed? | Can be resized? |
|---|---|---|---|---|
| String constant | const s = "abc" | Yes | N/A | No |
| Number constant | const n = 10 | Yes | N/A | No |
| Array | var a = [3]float32{1,2,3} | No | Yes | No |
| Slice | var s = []float32{1,2,3} | No | No | Yes |
Array vs slice in Go
| Feature | Array | Slice |
|---|---|---|
| Length is part of type | Yes | No |
| Fixed size | Yes | No |
Can use append | No | Yes |
| Good for known-size data | Yes | Sometimes |
Can be const | No | No |
const vs var
| Keyword | Use for | Example |
|---|---|---|
const | compile-time simple values | const pi = 3.14 |
var | runtime variables and all collections | var nums = []int{1, 2, 3} |
If your data is a collection, use var, not const.
Cheat Sheet
Quick rules
- Go
constworks for:- numbers
- strings
- booleans
- runes
- Go
constdoes not work for:- arrays
- slices
- maps
- structs
Valid examples
const name = "go"
const max = 10
const enabled = true
Invalid examples
const nums = []int{1, 2, 3}
const nums = [3]int{1, 2, 3}
Use these instead
Fixed-size array
var nums = [...]float32{1.1, 2.2, 3.3}
Slice
var nums = []float32{1.1, 2.2, 3.3}
Safer read-only style
var data = [...]float32{1.1, 2.2, 3.3}
func Data() []float32 {
out := make([]float32, len(data))
copy(out, data[:])
return out
}
Important edge case
A value can be logically constant in your design, but still not be a Go const.
That is normal for collections in Go.
FAQ
Why can't I declare a constant slice in Go?
Because slices are runtime data structures, not compile-time constant values.
Can I declare a constant array in Go?
No. Go does not allow arrays as const values either.
What should I use instead of a constant array?
Use a package-level var with an array or slice literal.
How do I make slice data read-only in Go?
Go has no built-in read-only slice type. A common pattern is to keep the original data private and return a copy.
Should I use an array or a slice for fixed data?
Use an array when the size is truly fixed and meaningful. Use a slice when you want more flexible APIs.
Is a string different from a slice in Go constants?
Yes. A string can be a constant, but a slice cannot.
Can callers modify a slice returned from a function?
Yes, unless you return a copy. Returning the original slice exposes shared mutable data.
Mini Project
Description
Build a small Go utility that stores English letter frequency data and looks up the score for a given lowercase letter. This demonstrates how to keep fixed reference data in Go using an array instead of an invalid const collection.
Goal
Create a program that maps letters a to z to their frequency values and prints the value for selected letters.
Requirements
- Store the alphabet as a string constant.
- Store the frequency values in a package-level array or slice.
- Write a function that accepts a lowercase letter and returns its frequency.
- Return
0for characters outsideatoz.
Keep learning
Related questions
Are Go Maps Passed by Value or Reference?
Learn Go map parameter and return semantics: maps are passed by value, while copies share underlying data. Know when map pointers are useful.
Authentication for Go REST APIs: Sessions, Tokens, and Middleware
Learn practical authentication patterns for Go REST APIs, including password hashing, server sessions, bearer tokens, middleware, and security rules.
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.