The code below
const s = "golang.go"
var a byte = 1 << len(s) / 128
The result of a
is 4. However, after changing const s
to var s
as following
var s = "golang.go"
var a byte = 1 << len(s) / 128
The result of a
is 0 now.
Also other test codes as below
const s = "golang.go"
var a byte = 1 << len(s) / 128 // the result of a is 4
var b byte = 1 << len(s[:]) / 128 // the result of b is 0
var ss = "golang.go"
var aa byte = 1 << len(ss) / 128 // the result of aa is 0
var bb byte = 1 << len(ss[:]) / 128 // the result of bb is 0
It is weird that b
is 0 with evaluating the length of s[:]
I try to understand it per golang spec
The expression len(s) is constant if s is a string constant. The expressions len(s) and cap(s) are constants if the type of s is an array or pointer to an array and the expression s does not contain channel receives or (non-constant) function calls
But I failed. Could someone explain it more clearly to me?
This behavior is somehow different when it comes to objects declared with const. While a const object cannot be updated, the properties of this objects can be updated. Therefore, if we declare a const object as this: greeting = { words: "Hello", number: "five" } // error: Assignment to constant variable.
Variables declared with the const maintain constant values. const declarations share some similarities with let declarations. Like let declarations, const declarations can only be accessed within the block they were declared. This means that the value of a variable declared with const remains the same within its scope.
The scope of a let variable is block scope. The scope of a const variable is block scope. It can be updated and re-declared into the scope. It can be updated but cannot be re-declared into the scope. It cannot be updated or re-declared into the scope.
Example 2: Users cannot change the properties of the const object, but they can change the value of properties of the const object. The scope of a var variable is functional scope. The scope of a let variable is block scope. The scope of a const variable is block scope. It can be updated and re-declared into the scope.
The difference is that when s
is constant, the expression is interpreted and executed as a constant expression, using untyped integer type and resulting in int
type. When s
is a variable, the expression is interpreted and executed as a non-constant expression, using byte
type.
Spec: Operators:
The right operand in a shift expression must have integer type or be an untyped constant representable by a value of type
uint
. If the left operand of a non-constant shift expression is an untyped constant, it is first implicitly converted to the type it would assume if the shift expression were replaced by its left operand alone.
The quoted part applies when s
is a variable. The expression is a non-constant shift expression (1 << len(s)
) because s
is a variable (so len(s)
is non-constant), and the left operand is an untyped constant (1
). So 1
is converted to a type it would assume if the shift expression were replaced by its left operand alone:
var a byte = 1 << len(s) / 128
replaced to
var a byte = 1 / 128
In this variable declaration byte
type will be used because that type is used for the variable a
. So back to the original: byte(1)
shifted left by 9
will be 0
, dividing it by 128
will also be 0
.
And when s
is constant, int
will be used because Spec: Constant expressions:
If the left operand of a constant shift expression is an untyped constant, the result is an integer constant; otherwise it is a constant of the same type as the left operand, which must be of integer type.
Here 1
will not be converted to byte
but 1 << len(s)
=> 1 << 9
will be 512
, divided by 128
will be 4
.
Constant in Go behave differently than you might expect. They are "arbitrary precision and _un_typed".
With const consts = "golang.go"
the expression 1 << len(consts) / 128
is a constant expression and evaluated as a constant expression with arbitrary precision resulting in an untyped integer 4
which can be assigned to a byte resulting in a == 4
.
With var vars = "golang.go"
the expression 1 << len(vars) / 128
no longer is a constant expression but has to be evaluated as some typed int. How is defined in https://go.dev/ref/spec#Operators
The right operand in a shift expression must have integer type or be an untyped constant representable by a value of type uint. If the left operand of a non-constant shift expression is an untyped constant, it is first implicitly converted to the type it would assume if the shift expression were replaced by its left operand alone.
The second sentence applies to your problem. The 1
is converted to "the type it would [read will] assume". Spelled out this is byte(1) << len(vars)
which is 0.
https://go.dev/blog/constants
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With