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?
CodePudding user response:
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.
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
.
CodePudding user response:
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.