Home > Software engineering >  Different behavior of len() with const or non-const value
Different behavior of len() with const or non-const value

Time:11-29

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.

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.

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.

https://go.dev/blog/constants

  • Related