Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different behavior of len() with const or non-const value

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?

like image 523
zangw Avatar asked Nov 29 '21 09:11

zangw


People also ask

What is the difference between a const and a constant variable?

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.

What is the difference between let and const variable in Python?

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.

What is the scope of a let and const variable?

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.

Can we change the properties of a const variable?

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.


Video Answer


2 Answers

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.

like image 58
icza Avatar answered Oct 26 '22 16:10

icza


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

like image 26
Volker Avatar answered Oct 26 '22 17:10

Volker