261

I have tried:

const ascii = "abcdefghijklmnopqrstuvwxyz"
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 first declaration and initialization works fine, but the second, third and fourth don't work.

How can I declare and initialize a const array of floats?

Pang
  • 9,564
  • 146
  • 81
  • 122
ceth
  • 44,198
  • 62
  • 180
  • 289

5 Answers5

367

An array isn't immutable by nature; you can't make it constant.

The nearest you can get is:

var 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 }

Note the [...] instead of []: it ensures you get a (fixed size) array instead of a slice. So the values aren't fixed but the size is.

As pointed out by @jimt, the [...]T syntax is sugar for [123]T. It creates a fixed size array, but lets the compiler figure out how many elements are in it.

blackgreen
  • 34,072
  • 23
  • 111
  • 129
Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • 5
    I guess allowing constant arrays would require updates to the type system. Otherwise if you defined a function `f(xs [5]int)` you wouldn't know if the array passed was constant or mutable. – Thomas Ahle Jul 06 '14 at 16:13
  • Surprisingly, the compiler generates initialization code for each array entry... – Julio Guerra Nov 09 '19 at 10:09
91

From Effective Go:

Constants in Go are just that—constant. They are created at compile time, even when defined as locals in functions, and can only be numbers, characters (runes), strings or booleans. Because of the compile-time restriction, the expressions that define them must be constant expressions, evaluatable by the compiler. For instance, 1<<3 is a constant expression, while math.Sin(math.Pi/4) is not because the function call to math.Sin needs to happen at run time.

Slices and arrays are always evaluated during runtime:

var TestSlice = []float32 {.03, .02}
var TestArray = [2]float32 {.03, .02}
var TestArray2 = [...]float32 {.03, .02}

[...] tells the compiler to figure out the length of the array itself. Slices wrap arrays and are easier to work with in most cases. Instead of using constants, just make the variables unaccessible to other packages by using a lower case first letter:

var ThisIsPublic = [2]float32 {.03, .02}
var thisIsPrivate = [2]float32 {.03, .02}

thisIsPrivate is available only in the package it is defined. If you need read access from outside, you can write a simple getter function (see Getters in golang).

Pang
  • 9,564
  • 146
  • 81
  • 122
fasmat
  • 3,353
  • 1
  • 15
  • 13
16

There is no such thing as array constant in Go.

Quoting from the Go Language Specification: Constants:

There are boolean constants, rune constants, integer constants, floating-point constants, complex constants, and string constants. Rune, integer, floating-point, and complex constants are collectively called numeric constants.

A Constant expression (which is used to initialize a constant) may contain only constant operands and are evaluated at compile time.

The specification lists the different types of constants. Note that you can create and initialize constants with constant expressions of types having one of the allowed types as the underlying type. For example this is valid:

func main() {
    type Myint int
    const i1 Myint = 1
    const i2 = Myint(2)
    fmt.Printf("%T %v\n", i1, i1)
    fmt.Printf("%T %v\n", i2, i2)
}

Output (try it on the Go Playground):

main.Myint 1
main.Myint 2

If you need an array, it can only be a variable, but not a constant.

I recommend this great blog article about constants: Constants

icza
  • 389,944
  • 63
  • 907
  • 827
  • then what do if need an container with constant size? –  Mar 31 '15 at 10:16
  • @Atomic_alarm Can you elaborate please? – icza Mar 31 '15 at 10:17
  • Exists whether in the golang analogue C-array? –  Mar 31 '15 at 10:42
  • 1
    @Atomic_alarm Yes, arrays do exist in Go too, they are just not constant expressions, they are evaluated at runtime. So a constant cannot be of an array type, but a variable can. For example: `var arr = [2]int{2, 3}` – icza Mar 31 '15 at 10:45
11

As others have mentioned, there is no official Go construct for this. The closest I can imagine would be a function that returns a slice. In this way, you can guarantee that no one will manipulate the elements of the original slice (as it is "hard-coded" into the array).

I have shortened your slice to make it...shorter...:

func GetLetterGoodness() []float32 {
    return []float32 { .0817,.0149,.0278,.0425,.1270,.0223 }
}
janreggie
  • 334
  • 1
  • 4
  • 12
Paul
  • 129
  • 1
  • 5
  • 4
    this sounds like the best way to go, however the func return type does not match. `cannot use [6]string literal (type [6]string) as type []string in return argument` so `return []float32 { ... }` – theRemix Jun 06 '17 at 21:50
  • @theRemix Three possible fixes: (1) remove `...` so as to declare a slice literal instead of an array literal. (2) change the return type to `[6]float32`. (3) assign the expression to an array variable, `a := [...]float32 { (etc.) }` and return the slice of all elements: `return a[:]`. (Array literals are not addressable, I'm not clear why.) – David Moles Jan 03 '19 at 17:46
  • I'm not sure what guarantees you mean. It sounds like you try to solve something in your code that is probably solved better with (unit-)tests. – Dynom Apr 09 '19 at 08:49
  • @Dynom each call will return a new instance of the slice. This guarantees, that nobody can change the "constant" for the whole application. For unit-tests you have to salt **all** your tests with a "no-change-check". And there is no guarantee, that new (untested) code not changing the value. – akop Apr 20 '22 at 06:17
0

In addition to @Paul's answer above, you can also do the following if you only need access to individual elements of the array (i.e. if you don't need to iterate on the array, get its length, or create slices out of it).

Instead of

var myArray [...]string{ /* ... */ }

you can do

func myConstArray(n int) string {
  return [...]string{ /* ... */ }[n]
}

and then instead of extracting elements as

str := myArray[i]

you extract them as

str := myConstArray(i)

Link on Godbolt: https://godbolt.org/z/8hz7E45eW (note how in the assembly of main no copy of the array is done, and how the compiler is able to even extract the corresponding element if n is known at compile time - something that is not possible with normal non-const arrays).

If instead, you need to iterate on the array or create slices out of it, @Paul's answer is still the way to go¹ (even though it will likely have a significant runtime impact, as a copy of the array needs to be created every time the function is called).

This is unfortunately the closest thing to const arrays we can get until https://github.com/golang/go/issues/6386 is solved.


¹ Technically speaking you can also do it with the const array as described in my answer, but it's quite ugly and definitely not very efficient at runtime: https://go.dev/play/p/rQEWQhufGyK

CAFxX
  • 28,060
  • 6
  • 41
  • 66