282

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?

6 Answers 6

396

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.

Sign up to request clarification or add additional context in comments.

3 Comments

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.
Surprisingly, the compiler generates initialization code for each array entry...
@ThomasAhle But why would it matter? In the signature f(xs [5]int) the array is passed by value, the same as f(xs0, xs1, xs2, xs3, xs4 int) so the mutability in question is of the copy in the stack, not the original constant.
100

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).

Comments

19

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

4 Comments

then what do if need an container with constant size?
@Atomic_alarm Can you elaborate please?
Exists whether in the golang analogue C-array?
@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}
14

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

4 Comments

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 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.)
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 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.
1

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

Comments

1

In my opinion, none of the existing answers really tell why there aren't constant arrays. In Go, arrays are passed by value. So it's not immediately obvious why, for example, the hypothetical array constant C = [2]string{"A", "B"} needs to be treated much differently than the equivalent sequence of scalar constants C0 = "A" and C1 = "B".

However, the problem is that arrays can be sliced and the result of slicing an array (type [N]T) is not another, smaller array but rather a slice (type []T). Since slices are inherently mutable, we reach a number of undesirable possibilities:

  • you can't slice constant arrays at all, making them not very useful and making the language more complicated
  • you can slice constant arrays, but you can't mutate them, requiring either a change to the language to add immutable slices (which would be nice, actually...) or a panic at runtime
  • you can slice constant arrays, and you can mutate them, because they've been copied onto the stack/heap, but then you'd have unintuitive semantics around copying/aliasing

Whereas, constant strings don't have this problem, because a string is immutable and slicing a string produces another string.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.