for Loop Semantic Changes in Go 1.22: Be Aware of the Impact
@@ -181,13 +47,15 @@
What are the changes?
We can install multiple Go toolchain versions to check the outputs. Here, I use the GoTV tool to (conveniently) choose Go toolchain versions.
-The outputs:
$ gotv 1.21. run demo1.go
+
+
+The outputs:
$ gotv 1.21. run demo1.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run demo1.go
14
$ gotv 1.22. run demo1.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run demo1.go
10
-
+
The behavior difference is obvious:
@@ -253,13 +121,13 @@
What are the changes?
-The outputs of the above program:
$ gotv 1.21. run demo2.go
+The outputs of the above program:
$ gotv 1.21. run demo2.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run demo2.go
12
$ gotv 1.22. run demo2.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run demo2.go
6
-
+
This article focuses on the details of the changes and impact of the changes, rather than the reasons behind them. For details on the approval process and reasons behind the changes, see
@@ -289,6 +157,8 @@
What are the changes?
+
+
The impact of the changes
@@ -310,6 +180,8 @@
The impact of the changes
+
+
By the speficication, since Go 1.22, the loop shown above is actually equivalent to the following pseudo-code (Sorry, the new semantics are hard to explain in a clear and perfect way. None of Go official documentations ever successfully achieve this goal. Here, I have tried my best.):
{
@@ -335,7 +207,7 @@
The impact of the changes
Wow, quite a lot of magical implicit code. For a language that promotes explicitness, it's embarrassing.
-Implicitness often leads to unexpected surprises, which is not a surprise. The following will show several examples which might break your expectations.
+Implicitness often leads to unexpected surprises, which is not a surprise. The following will show several cases which might break your expectations.
The behaviors of deferred function calls which capture loop variables might change
@@ -359,7 +231,7 @@
The behaviors of deferred function calls which capture loop variables might
-Its outputs:
$ gotv 1.21. run demo-defer.go
+Its outputs:
$ gotv 1.21. run demo-defer.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run demo-defer.go
#0: 0
#1: 1
@@ -369,7 +241,7 @@
The behaviors of deferred function calls which capture loop variables might
#0: 0
#0: 1
#0: 2
-
+
You can find that, since Go 1.22, the value of counter is never effectively increased. Why? I'm sorry. As mentioned above, it is some hard to clearly explain the new semantics and I don't think I have the ability to do this. You may get it from the following equivalent code:
@@ -385,7 +257,7 @@
The behaviors of deferred function calls which capture loop variables might
} else {
n--
}
-
+
if !(n >= 0) {
break
}
@@ -422,7 +294,7 @@
The behaviors of deferred function calls which capture loop variables might
}(i)
}
}
-
+
r = make([]int, count) // only allocate once
return
}
@@ -433,13 +305,13 @@
The behaviors of deferred function calls which capture loop variables might
-The outputs of the above program:
$ gotv 1.21. run search.go
+The outputs of the above program:
$ gotv 1.21. run search.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run search.go
[8 6 4 2 0]
$ gotv 1.22. run search.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run search.go
[0 0 0 0 0]
-
+
So, since Go 1.22, just be careful when using freshly-declared loop variables in deferred function calls.
@@ -454,6 +326,8 @@
The behaviors of deferred function calls which capture loop variables might
+
+
However, sadly, the suggestion was ignored totally.
@@ -485,13 +359,13 @@
Be careful when capturing loop variables in closures
-Its outputs:
$ gotv 1.21. run demo-closure-1.go
+Its outputs:
$ gotv 1.21. run demo-closure-1.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run demo-closure-1.go
9
$ gotv 1.22. run demo-closure-1.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run demo-closure-1.go
0
-
+
Prior to Go 1.22, what the printN closure captures is the only instance of the loop variable, which final value is 9. However, since Go 1.22, what the printN closure captures is the first instance of the loop variable, which final value is 1. That is the reason of the behavior difference between the two Go versions.
@@ -521,13 +395,13 @@
Be careful when capturing loop variables in closures
-Its outputs:
$ gotv 1.21. run demo-closure-2.go
+Its outputs:
$ gotv 1.21. run demo-closure-2.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run demo-closure-2.go
abcdefghijklmnopqrstuvwxyz
$ gotv 1.22. run demo-closure-2.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run demo-closure-2.go
a
-
+
The third example:
@@ -554,9 +428,7 @@
Be careful when capturing loop variables in closures
-
-
Be careful when taking addresses of loop variables
-
+#:::::::::::::::::::::::::::::::::::::::::::::::::; Be careful when taking addresses of loop variables
Similarly, since Go 1.22, it may be dangerous to use the address of a freshly-declared loop variable across loop iterations.
@@ -575,13 +447,13 @@
Be careful when taking addresses of loop variables
-Its outputs:
$ gotv 1.21. run demo-pointer1.go
+Its outputs:
$ gotv 1.21. run demo-pointer1.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run demo-pointer1.go
true
$ gotv 1.22. run demo-pointer1.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run demo-pointer1.go
false
-
+
Go 1.21 and 1.22 give different answers. Why? From the equivalent code shown below, we can get that, in the comparison p == &i, p points to the first instance of i, whereas &i takes the address of the second instance of i. So the comparison evaluation result is false.
@@ -624,7 +496,7 @@
Be careful when taking addresses of loop variables
-Since Go 1.22, the above program will never exit (prior to Go 1.22, it will):
$ gotv 1.21. run demo-pointer2.go
+Since Go 1.22, the above program will never exit (prior to Go 1.22, it will):
$ gotv 1.21. run demo-pointer2.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run demo-pointer2.go
0
1
@@ -635,12 +507,10 @@
Be careful when taking addresses of loop variables
0
0
...
-
+
-
-
Be careful when moving the 3rd clause statements inside loop bodies
-
+#::::::::::::::::::::::::::::::::::::::::::::::::; Be careful when moving the 3rd clause statements inside loop bodies
Since Go 1.22, the following two loops might be not equivalent with each other any more (prior to Go 1.22, they are equivalent).
@@ -686,18 +556,16 @@
Be careful when moving the 3rd clause statements inside loop bodies
-The new outputs:
$ gotv 1.22. run demo-pointer3.go
+The new outputs:
$ gotv 1.22. run demo-pointer3.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run demo-pointer3.go
true
0
1
2
-
+
-
-
Be careful when declaring no-copy values as loop variables
-
+#::::::::::::::::::::::::::::::::::::::::::::::::::; Be careful when declaring no-copy values as loop variables
As explained above, since Go 1.22, at the start of each loop iteration, each freshly-declared loop variable will get copied once, implicitly. The implication means that, since Go 1.22, it is not a good idea to declare no-copy values as loop variables, such as sync.Mutex, sync/atomic.Int64, bytes.Buffer, and strings.Builder values etc.
@@ -716,7 +584,7 @@
Be careful when declaring no-copy values as loop variables
Be careful when declaring no-copy values as loop variables
-Its outputs:
$ gotv 1.21. run demo-nocopy1.go
+Its outputs:
$ gotv 1.21. run demo-nocopy1.go
[Run]: $HOME/.cache/gotv/tag_go1.21.8/bin/go run demo-nocopy1.go
0
2
@@ -745,7 +613,7 @@
Be careful when declaring no-copy values as loop variables
0
$ gotv 1.22. vet demo-nocopy1.go
[Run]: $HOME/.cache/gotv/tag_go1.22.1/bin/go vet demo-nocopy1.go
-
+
Note that the go vet command in Go 1.22 toolchain can't catch such implicit duplication of no-copy values, regardless of whether the loop variable wg is captured in the loop body or not.
@@ -781,7 +649,7 @@
Be careful when declaring no-copy values as loop variables
-Run it with different Go toolchains, we get:
$ gotv 1.21. run demo-nocopy2.go
+Run it with different Go toolchains, we get:
$ gotv 1.21. run demo-nocopy2.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run demo-nocopy2.go
abcdefghijklmnopqrstuvwxyz
$ gotv 1.22. run demo-nocopy2.go
@@ -790,7 +658,7 @@
Be careful when declaring no-copy values as loop variables
goroutine 1 [running]:
...
-
+
Yes, the run-time no-copy check works. Since Go 1.22, when the loop variable of type strings.Builder gets duplicated, a panic is created. Prior to Go 1.22, this duplication will not happen, so there will be no panic.
@@ -799,20 +667,22 @@
Be careful when declaring no-copy values as loop variables
Let's change the Debug constant in the above example to false, then run the example again with the 1.22 toolchain.
-
$ gotv 1.22. run demo-nocopy.go
+
$ gotv 1.22. run demo-nocopy.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run demo-nocopy.go
abcdefghijklmnopqrstuvwxyz
-
+
We can find that no panic occurs now. Why? Because now the if (Debug) { callback(&b) } line becomes into dead code so that the compiler thinks that each instance of the loop variable b is used solely within the corresponding iteration's lifetime. So the loop variable b is instantiated only once for the entire loop and no duplication happens for the only instance.
However, the compiler is too smart to make a bad decision here. The compiler incorrectly implements the semantics. The example program should panic regardless of the value of the Debug constant. While this specific case might be considered acceptable due to the lack of harmful consequences, it raises concerns about the potential for unexpected behavior in other scenarios.
+
+
The safe advice is try not to declare no-copy values as loop variables. This is just a suggestion, not a mandatory rule, because copying no-copy values does not always cause damage (but the damage may be exposed later when the code is refactored in some way).
-
Warning: the performance of your Go programs might be degraded silently
+
Warning: the performance of your Go programs might be degraded silently
Sometimes, a compiler is over smart; sometimes, it is not smart enough. For example, sometimes, the official standard compiler provided in Go toolchain 1.22 is unable to determine that each instance of a freshly-declared loop variable is used solely within the corresponding iteration's lifetime, so that the loop variable will be instantiated per iteration and each of its instances will be allocated on heap instead of stack. Even worse, if the size of the loop variable is large, then high duplication costs will be incurred. When these situations occur, the performance of the program will be degraded.
@@ -855,7 +725,7 @@
Warning: the performance of your Go programs might be degraded silently
-Its outputs:
$ gotv 1.21. run aaa.go
+Its outputs:
$ gotv 1.21. run aaa.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run aaa.go
foo time: 3.573µs
bar time: 3.267µs
@@ -863,7 +733,7 @@
Warning: the performance of your Go programs might be degraded silently
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run aaa.go
foo time: 3.819µs
bar time: 552.246µs
-
+
The benchmark results reveal a significant performance regression in the bar function between Go 1.21 and 1.22. Why? Because, with the official standard Go compiler 1.22, the loop variable a in the bar function is duplicated in each iteration. Whereas in prior versions, such duplication is always needless.
@@ -912,7 +782,7 @@
Warning: things might become more subtle than before when loop variables are
-The above program is intended to print the values of the loop variable i at each iteration. Prior to Go 1.22, there is a clear data race condition present in the program, because the loop variable i is only instantiated once during the whole loop. All the new created goroutines will read the single instance but the main goroutine will modify it. The following outputs prove this fact:
$ CGO_ENABLED=1 gotv 1.21. run -race demo-concurency1.go
+The above program is intended to print the values of the loop variable i at each iteration. Prior to Go 1.22, there is a clear data race condition present in the program, because the loop variable i is only instantiated once during the whole loop. All the new created goroutines will read the single instance but the main goroutine will modify it. The following outputs prove this fact:
$ CGO_ENABLED=1 gotv 1.21. run -race demo-concurency1.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run -race demo-concurency1.go
3
3
@@ -921,15 +791,15 @@
Warning: things might become more subtle than before when loop variables are
...
==================
3
-
+
-Prior to Go 1.22, the fix is simple, just add an i := i line at the start of the loop body. Go 1.22 fixes the specified data race problem by changing the semantics of for;; loops, without modifying the old problematic code. This can be verified by the following outputs:
$ CGO_ENABLED=1 gotv 1.22. run -race demo-concurency1.go
+Prior to Go 1.22, the fix is simple, just add an i := i line at the start of the loop body. Go 1.22 fixes the specified data race problem by changing the semantics of for;; loops, without modifying the old problematic code. This can be verified by the following outputs:
$ CGO_ENABLED=1 gotv 1.22. run -race demo-concurency1.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run -race demo-concurency1.go
1
2
0
-
+
In fact, this is just the reason why Go 1.22 made the semantic change to for;; loops. But is it worth it to fix such a small problem by introducing magical implicit code?
@@ -961,7 +831,7 @@
Warning: things might become more subtle than before when loop variables are
Is the new code still data race free (with Go 1.22 semantics)? It looks good. Each new created goroutine just uses an exclusive copy of the loop variable i. But the answer is "no", because there is an implicit assignment at the start of each iteration and the implicit assignment uses an instance of the loop variable as source value (a.k.a. the main goroutine reads it), however the instance is modified in a new created goroutine.
-The following outputs verify there is a data race condition present in the new code:
$ CGO_ENABLED=1 gotv 1.22. run -race demo-concurency2.go
+The following outputs verify there is a data race condition present in the new code:
$ CGO_ENABLED=1 gotv 1.22. run -race demo-concurency2.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run -race demo-concurency2.go
==================
WARNING: DATA RACE
@@ -971,7 +841,7 @@
Warning: things might become more subtle than before when loop variables are
1
3
Found 1 data race(s)
-
+
Prior to Go 1.22, the data race is clear and easily to detect. But since Go 1.22, things become more subtle and the data race is not very clear (because of the implicit code).
@@ -1004,14 +874,14 @@
Warning: things might become more subtle than before when loop variables are
i++
v := i
m.Unlock()
-
+
if isGold(v) {
c <- v
}
}
}()
}
-
+
for n := range c {
fmt.Println("Found gold", n)
}
@@ -1019,7 +889,7 @@
Warning: things might become more subtle than before when loop variables are
-Run it with different toolchain versions, get the following outputs:
$ CGO_ENABLED=1 gotv 1.21. run -race demo-concurency3.go
+Run it with different toolchain versions, get the following outputs:
$ CGO_ENABLED=1 gotv 1.21. run -race demo-concurency3.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run -race demo-concurency3.go
Found gold 1048576
Found gold 2097152
@@ -1043,7 +913,7 @@
Warning: things might become more subtle than before when loop variables are
Found gold 3145728
...
^C
-
+
😳😳😳... (Consider that the title of the proposal to make the semantic change is "Proposal: Less Error-Prone Loop Variable Scoping".)
@@ -1082,6 +952,8 @@
Specify Go language versions for Go source files
+
+
If the Go language version of a Go source file is not specified by any of the above ways, then the version of the used Go compiler is used. In other words, the behavior of the code in the source file is compiler dependent.
The design causes two problems:
@@ -1098,6 +970,8 @@
Specify Go language versions for Go source files
+
+
Anyway, since Go 1.22, you should try to specify a Go language version for every Go source file, in any of the above introduced ways, to avoid compiler version dependent behaviors. This is the minimum standard to be a professional Go programmer in the Go 1.22+ era.
@@ -1157,7 +1031,7 @@
Final words
In my honest opinion, the benefits of the new semantics of for;; loops are rare and tiny, whereas the drawbacks are more prominent and serious.
-The semantic changes introduced in Go 1.22 significantly lower the threshold for maintaining backward compatibility. This is a bad start.
+The semantic changes introduced in Go 1.22 significantly lower the standard for maintaining backward compatibility. This is a bad start.
I have expressed my opinions in the following comments:
@@ -1185,7 +1059,3 @@
Final words
What's done is done. In the end, I hope this article will help you write professional Go code in the Go 1.22+ era.
-
-
-
-
\ No newline at end of file
diff --git a/pages/blog/2024-03-01-for-loop-semantic-changes-in-go-1.22.tmd b/pages/blog/2024-03-01-for-loop-semantic-changes-in-go-1.22.tmd
index 66ec5146..ec5438fb 100644
--- a/pages/blog/2024-03-01-for-loop-semantic-changes-in-go-1.22.tmd
+++ b/pages/blog/2024-03-01-for-loop-semantic-changes-in-go-1.22.tmd
@@ -1,5 +1,6 @@
-# `for` Loop Semantic Changes in Go 1.22: Be Aware of the Impact
+#######################################################
+ `for` Loop Semantic Changes in Go 1.22: Be Aware of the Impact
Go 1.22 changed the semantics of `for` loops,
including both `for-range` loops and traditional
@@ -11,7 +12,8 @@ understand the implications of these changes
in order to write Go code which will behave as intended.
Otherwise, your code may exhibit unexpected behavior.
-## What are the changes?
+#========================================
+ What are the changes?
Specifically speaking, only the semantics of the `for` loops
which loop variables are declared within the loops are changed
@@ -21,13 +23,13 @@ For example, in the following piece of code,
the semantics of the former two loops are not changed,
but the latter two ones are changed (from Go 1.21 to 1.22).
-```Go
+'''Go
for k, v = range aContainer {...}
for a, b, c = f(); condition; statement {...}
for k, v := range aContainer {...}
for a, b, c := f(); condition; statement {...}
-```
+'''
The former two loops don't declare their respective loop variables,
but the latter two do. That is the difference here.
@@ -36,7 +38,7 @@ The semantics of the former two loops are not changed.
Let's view a simple Go program which undergoes semantic change
(and behavior change) from Go 1.21 to Go 1.22:
-```Go
+'''Go
//demo1.go
package main
@@ -55,22 +57,22 @@ func main() {
println(<-out + <-out)
}
-```
+'''
We can install multiple Go toolchain versions to check the outputs.
Here, I use the __GoTV__ tool to (conveniently) choose Go toolchain versions.
-@__GoTV `` https://go101.org/apps-and-libs/gotv.html
+////__GoTV `` https://go101.org/apps-and-libs/gotv.html
The outputs:
-```
+'''
$ gotv 1.21. run demo1.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run demo1.go
14
$ gotv 1.22. run demo1.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run demo1.go
10
-```
+'''
The behavior difference is obvious:
* prior to Go 1.22, it printed `14` (very probably, when without the participation of the channel `c`);
@@ -98,14 +100,14 @@ which should be a clear fact for a competent Go programmer.
In order to avoid data race and get the same result as the new semantics,
the loop in the program should be re-written as:
-```Go
+'''Go
for i, v := range m {
i, v := i, v // this line is added
go func() {
out <- i+v
}()
}
-```
+'''
Under the new semantics, the added line becomes unnecessary.
In fact, this is the main reason why the semantic changes were made in Go 1.22.
@@ -113,7 +115,7 @@ In fact, this is the main reason why the semantic changes were made in Go 1.22.
Similarly, the following program also undergoes
semantic/behavior change from Go 1.21 to Go 1.22:
-```Go
+'''Go
// demo2.go
package main
@@ -131,17 +133,17 @@ func main() {
println(<-out + <-out + <-out)
}
-```
+'''
The outputs of the above program:
-```
+'''
$ gotv 1.21. run demo2.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run demo2.go
12
$ gotv 1.22. run demo2.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run demo2.go
6
-```
+'''
This article focuses on the details of the changes
and impact of the changes, rather than the reasons behind them.
@@ -162,9 +164,10 @@ see
the release maintains __the Go 1 promise of compatibility__. However,
this is simply not the case (read below for reasons).
- @__the Go 1 promise of compatibility `` https://go.dev/doc/go1compat
+ ////__the Go 1 promise of compatibility `` https://go.dev/doc/go1compat
-## The impact of the changes
+#==========================================
+ The impact of the changes
Personally, I think the rationale of the change to `for-range` loops is well-justified.
The new semantics of `for-range` loops become more intuitive.
@@ -178,11 +181,11 @@ with `for-range` loops (they are both `for` loops).
However, It's not intuitive at all to think the loop variables in the following alike
loops are per-iteration scoped.
-```Go
+'''Go
for a, b, c := anExpression; aCondition; postStatement {
... // loop body
}
-```
+'''
The `a, b, c := anExpression` statement is only executed once
during the execution of the loop, so it is intuitive that
@@ -198,7 +201,7 @@ code to do the job. This is true. __Go 1.22+ specification__ says:
before executing the post statement and initialized to the value of
the previous iteration's variable at that moment.
-@__... specification``https://go.dev/ref/spec#For_statements
+////__... specification``https://go.dev/ref/spec#For_statements
By the speficication, since Go 1.22, the loop shown above is
actually equivalent to the following pseudo-code (%%Sorry,
@@ -206,7 +209,7 @@ the new semantics are hard to explain in a clear and perfect way.
None of Go official documentations ever successfully achieve this goal.
Here, I have tried my best.%%):
-```Go
+'''Go
{
a_last, b_last, c_last := anExpression
pa_last, pb_last, pc_last = &a_last, &b_last, &c_last
@@ -225,19 +228,20 @@ Here, I have tried my best.%%):
... // loop body
}
}
-```
+'''
Wow, quite a lot of magical implicit code.
For a language that promotes explicitness, it's embarrassing.
Implicitness often leads to unexpected surprises, which is not a surprise.
-The following will show several examples which might break your expectations.
+The following will show several cases which might break your expectations.
-### The behaviors of deferred function calls which capture loop variables might change
+#:::::::::::::::::::::::::::::::::::::
+ The behaviors of deferred function calls which capture loop variables might change
A simple example:
-```Go
+'''Go
// demo-defer.go
package main
@@ -251,10 +255,10 @@ func main() {
}(n)
}
}
-```
+'''
Its outputs:
-```
+'''
$ gotv 1.21. run demo-defer.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run demo-defer.go
#0: 0
@@ -265,7 +269,7 @@ $ gotv 1.22. run demo-defer.go
#0: 0
#0: 1
#0: 2
-```
+'''
You can find that, since Go 1.22, the value of `counter`
is never effectively increased. Why? I'm sorry.
@@ -273,7 +277,7 @@ As mentioned above, it is some hard to clearly explain
the new semantics and I don't think I have the ability to do this.
You may get it from the following equivalent code:
-```Go
+'''Go
func main() {
counter_last, n_last := 0, 2
p_counter_last, p_n_last := &counter_last, &n_last
@@ -296,11 +300,11 @@ func main() {
}(n)
}
}
-```
+'''
A more realistic example:
-```Go
+'''Go
// search.go
package main
@@ -330,17 +334,17 @@ func search(start, end int)(r []int) {
func main() {
fmt.Println(search(0, 9))
}
-```
+'''
The outputs of the above program:
-```
+'''
$ gotv 1.21. run search.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run search.go
[8 6 4 2 0]
$ gotv 1.22. run search.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run search.go
[0 0 0 0 0]
-```
+'''
So, since Go 1.22, just be careful when using freshly-declared loop variables
in deferred function calls.
@@ -353,20 +357,21 @@ For example, in the following loop code, `n` is per-iteration scoped but `counte
is whole-loop scoped.
{
-```Go
+'''Go
for counter, n := 0, 2; n >= 0; n := n - 1 { ... }
-```
+'''
-@__allow re-declaration... ``https://github.com/golang/go/issues/60078#issuecomment-1547130632
+////__allow re-declaration... ``https://github.com/golang/go/issues/60078#issuecomment-1547130632
However, sadly, the suggestion was ignored totally.
}
-### Be careful when capturing loop variables in closures
+#:::::::::::::::::::::::::::::::::::::::::::::
+ Be careful when capturing loop variables in closures
An example:
-```Go
+'''Go
// demo-closure-1.go
package main
@@ -383,17 +388,17 @@ func main() {
}
printN()
}
-```
+'''
Its outputs:
-```
+'''
$ gotv 1.21. run demo-closure-1.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run demo-closure-1.go
9
$ gotv 1.22. run demo-closure-1.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run demo-closure-1.go
0
-```
+'''
Prior to Go 1.22, what the `printN` closure captures is the only instance
of the loop variable, which final value is `9`.
@@ -403,7 +408,7 @@ That is the reason of the behavior difference between the two Go versions.
Here is a similar example:
-```Go
+'''Go
// demo-closure-2.go
package main
@@ -424,21 +429,21 @@ func main() {
}
printBuf()
}
-```
+'''
Its outputs:
-```
+'''
$ gotv 1.21. run demo-closure-2.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run demo-closure-2.go
abcdefghijklmnopqrstuvwxyz
$ gotv 1.22. run demo-closure-2.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run demo-closure-2.go
a
-```
+'''
The third example:
-```Go
+'''Go
package main
func main() {
@@ -450,7 +455,7 @@ func main() {
}
}
}
-```
+'''
It will never exit since Go 1.22 (prior to Go 1.22, it prints `012` then exits immediately)
@@ -459,7 +464,8 @@ since Go 1.22, a freshly-declared loop variable may have many instances at run t
whether or not it is modified in `postStatement`.
Each of the instances is instantiated in one iteration.
-### Be careful when taking addresses of loop variables
+#:::::::::::::::::::::::::::::::::::::::::::::::::;
+ Be careful when taking addresses of loop variables
Similarly, since Go 1.22, it may be dangerous to use
the address of a freshly-declared loop variable across loop iterations.
@@ -469,7 +475,7 @@ For example, what does the following Go program print?
should not be kept for such cases. What a ridiculous point.
The code in reality may be more bizarre than this!)%%
-```Go
+'''Go
// demo-pointer1.go
package main
@@ -480,17 +486,17 @@ func main() {
p = &i
}
}
-```
+'''
Its outputs:
-```
+'''
$ gotv 1.21. run demo-pointer1.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run demo-pointer1.go
true
$ gotv 1.22. run demo-pointer1.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run demo-pointer1.go
false
-```
+'''
Go 1.21 and 1.22 give different answers.
Why? From the equivalent code shown below, we can get that,
@@ -498,7 +504,7 @@ in the comparison `p == &i`, `p` points to the first instance of `i`,
whereas `&i` takes the address of the second instance of `i`.
So the comparison evaluation result is `false`.
-```Go
+'''Go
func main() {
i_last, p_last := 0, (*int)(nil)
p_i_last, p_p_last := &i_last, &p_last
@@ -517,11 +523,11 @@ func main() {
p = &i
}
}
-```
+'''
Another example:
-```Go
+'''Go
// demo-pointer2.go
package main
@@ -534,10 +540,10 @@ func main() {
fmt.Println(i)
}
}
-```
+'''
Since Go 1.22, the above program will never exit (prior to Go 1.22, it will):
-```
+'''
$ gotv 1.21. run demo-pointer2.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run demo-pointer2.go
0
@@ -549,14 +555,15 @@ $ gotv 1.22. run demo-pointer2.go
0
0
...
-```
+'''
-### Be careful when moving the 3rd clause statements inside loop bodies
+#::::::::::::::::::::::::::::::::::::::::::::::::;
+ Be careful when moving the 3rd clause statements inside loop bodies
Since Go 1.22, the following two loops might be not equivalent with each other any more
(prior to Go 1.22, they are equivalent).
-```Go
+'''Go
for ...; ...; postStatement {
... // no continue statements here
}
@@ -565,12 +572,12 @@ for ...; ...; {
... // no continue statements here
postStatement
}
-```
+'''
For example, if we move the 3rd clause statements of the loops in the last section
into loop bodies, then their behaviors change (since Go 1.22).
-```Go
+'''Go
// demo-pointer3.go
package main
@@ -597,19 +604,20 @@ func main() {
pointerDemo1();
pointerDemo2();
}
-```
+'''
The new outputs:
-```
+'''
$ gotv 1.22. run demo-pointer3.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run demo-pointer3.go
true
0
1
2
-```
+'''
-### Be careful when declaring no-copy values as loop variables
+#::::::::::::::::::::::::::::::::::::::::::::::::::;
+ Be careful when declaring no-copy values as loop variables
As explained above, since Go 1.22, at the start of each loop iteration,
each freshly-declared loop variable will get copied once, **%%implicitly%%**.
@@ -622,7 +630,7 @@ For example, in Go versions prior to 1.22, the following code was considered con
However, starting with Go 1.22, this code is considered to have a concurrency issue,
because the loop variable `wg` will be (implicitly) copied at the start of each loop iteration.
-```Go
+'''Go
// demo-nocopy1.go
package main
@@ -652,10 +660,10 @@ func process() (wait func()) {
func main() {
process()()
}
-```
+'''
Its outputs:
-```
+'''
$ gotv 1.21. run demo-nocopy1.go
[Run]: $HOME/.cache/gotv/tag_go1.21.8/bin/go run demo-nocopy1.go
0
@@ -666,7 +674,7 @@ $ gotv 1.22. run demo-nocopy1.go
0
$ gotv 1.22. vet demo-nocopy1.go
[Run]: $HOME/.cache/gotv/tag_go1.22.1/bin/go vet demo-nocopy1.go
-```
+'''
Note that the `go vet` command in Go 1.22 toolchain can't catch such implicit duplication
of no-copy values, regardless of whether the loop variable `wg` is captured in the loop body or not.
@@ -675,7 +683,7 @@ Certain `no-copy` checks occur during run time.
Let's view an example which uses `strings.Builder` (each `strings.Builder` value
contains a pointer field which should point to itself):
-```Go
+'''Go
// demo-nocopy2.go
package main
@@ -702,10 +710,10 @@ func main() {
}
fmt.Println(a2z(debugProcess))
}
-```
+'''
Run it with different Go toolchains, we get:
-```
+'''
$ gotv 1.21. run demo-nocopy2.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run demo-nocopy2.go
abcdefghijklmnopqrstuvwxyz
@@ -715,7 +723,7 @@ panic: strings: illegal use of non-zero Builder copied by value
goroutine 1 [running]:
...
-```
+'''
Yes, the run-time `no-copy` check works.
Since Go 1.22, when the loop variable of type `strings.Builder` gets
@@ -731,11 +739,11 @@ the loop variable is used solely within the corresponding iteration's lifetime.
Let's change the `Debug` constant in the above example to `false`,
then run the example again with the 1.22 toolchain.
-```
+'''
$ gotv 1.22. run demo-nocopy.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run demo-nocopy.go
abcdefghijklmnopqrstuvwxyz
-```
+'''
We can find that no panic occurs now. Why? Because now the
`if (Debug) { callback(&b) }` line becomes into dead code
@@ -751,14 +759,15 @@ While this specific case might be considered acceptable due to
the lack of harmful consequences, it raises concerns about the potential
for unexpected behavior in other scenarios.
- @__make a bad decision `` https://github.com/golang/go/issues/66070
+ ////__make a bad decision `` https://github.com/golang/go/issues/66070
The safe advice is try not to declare no-copy values as loop variables.
This is just a suggestion, not a mandatory rule,
because copying no-copy values does not always cause damage
(but the damage may be exposed later when the code is refactored in some way).
-## Warning: the performance of your Go programs might be degraded silently
+#:::::::::::::::::::::::::::::::::::::::::::
+ Warning: the performance of your Go programs might be degraded silently
Sometimes, a compiler is over smart; sometimes, it is not smart enough.
For example, sometimes, the official standard compiler provided in Go toolchain 1.22 is
@@ -772,7 +781,7 @@ When these situations occur, the performance of the program will be degraded.
Let's view an example, in which a large-size loop variable is used
in the `bar` function.
-```Go
+'''Go
// demo-largesize.go
package main
@@ -806,10 +815,10 @@ func main() {
fmt.Println("foo time:", bench(foo))
fmt.Println("bar time:", bench(bar))
}
-```
+'''
Its outputs:
-```
+'''
$ gotv 1.21. run aaa.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run aaa.go
foo time: 3.573µs
@@ -818,7 +827,7 @@ $ gotv 1.22. run aaa.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run aaa.go
foo time: 3.819µs
bar time: 552.246µs
-```
+'''
The benchmark results reveal a significant performance regression
in the `bar` function between Go 1.21 and 1.22.
@@ -836,12 +845,12 @@ Suggestions to avoid such performance degradation issue:
the loop itself to optimize performance. This is beneficial if you can
guarantee that the variables don't need to be instantiated in each iteration.
-### Warning: things might become more subtle than before
- when loop variables are used concurrently
+#::::::::::::::::::::::::::::::::::::::::::::::::
+ Warning: things might become more subtle than before when loop variables are used concurrently
Firstly, let's view a simple program.
-```Go
+'''Go
// demo-concurency1.go
package main
@@ -861,14 +870,14 @@ func main() {
}
wg.Wait()
}
-```
+'''
The above program is intended to print the values of the loop variable `i` at each iteration.
Prior to Go 1.22, there is a clear data race condition present in the program,
because the loop variable `i` is only instantiated once during the whole loop.
All the new created goroutines will read the single instance but the main goroutine
will modify it. The following outputs prove this fact:
-```
+'''
$ CGO_ENABLED=1 gotv 1.21. run -race demo-concurency1.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run -race demo-concurency1.go
3
@@ -878,18 +887,18 @@ WARNING: DATA RACE
...
==================
3
-```
+'''
Prior to Go 1.22, the fix is simple, just add an `i := i` line at the start of the loop body.
Go 1.22 fixes the specified data race problem by changing the semantics of `for;;` loops,
without modifying the old problematic code. This can be verified by the following outputs:
-```
+'''
$ CGO_ENABLED=1 gotv 1.22. run -race demo-concurency1.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run -race demo-concurency1.go
1
2
0
-```
+'''
In fact, this is just the reason why Go 1.22 made the semantic change to `for;;` loops.
But is it worth it to fix such a small problem by introducing magical implicit code?
@@ -897,7 +906,7 @@ But is it worth it to fix such a small problem by introducing magical implicit c
The effect of the attempt to fix the problem by making semantic change is actually limited.
Let's modify the above program a bit:
-```Go
+'''Go
// demo-concurency2.go
package main
@@ -918,7 +927,7 @@ func main() {
}
wg.Wait()
}
-```
+'''
Is the new code still data race free (with Go 1.22 semantics)?
It looks good. Each new created goroutine just uses an exclusive copy
@@ -929,7 +938,7 @@ variable as source value (a.k.a. the main goroutine reads it),
however the instance is modified in a new created goroutine.
The following outputs verify there is a data race condition present in the new code:
-```
+'''
$ CGO_ENABLED=1 gotv 1.22. run -race demo-concurency2.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run -race demo-concurency2.go
==================
@@ -940,7 +949,7 @@ WARNING: DATA RACE
1
3
Found 1 data race(s)
-```
+'''
Prior to Go 1.22, the data race is clear and easily to detect.
But since Go 1.22, things become more subtle
@@ -955,7 +964,7 @@ This can significantly delay the identification and resolution of the problem!
More seriously, some old good concurrent code will become problematic.
Here is an example:
-```Go
+'''Go
// demo-concurency3.go
package main
@@ -992,10 +1001,10 @@ func main() {
fmt.Println("Found gold", n)
}
}
-```
+'''
Run it with different toolchain versions, get the following outputs:
-```
+'''
$ CGO_ENABLED=1 gotv 1.21. run -race demo-concurency3.go
[Run]: $HOME/.cache/gotv/tag_go1.21.7/bin/go run -race demo-concurency3.go
Found gold 1048576
@@ -1020,7 +1029,7 @@ Found gold 3145728
Found gold 3145728
...
^C
-```
+'''
😳😳😳... %%(Consider that the title of the proposal to make the semantic change is
"Proposal: Less Error-Prone Loop Variable Scoping".)
@@ -1030,7 +1039,8 @@ We can still use the old trick: just add an `i := i` line at the start of the lo
Yes, this is still the best suggestion to avoid data race for such situations in the Go 1.22+ era.
Is this a mockery of the new semantics (of `for;;` loops)?
-## Advice and suggestions
+#==============================================
+ Advice and suggestions
Okay, the above are the potential issues I've identified so far
with the new `for;;` semantics introduced in Go 1.22.
@@ -1038,7 +1048,8 @@ There might be more, I'm not sure.
Here are some recommendations you can follow in the Go 1.22+ era.
-### Specify Go language versions for Go source files
+#::::::::::::::::::::::::::::::::::::::::::::
+ Specify Go language versions for Go source files
As demonstrated in many above examples, the semantic changes made
in Go 1.22 break backward-compatibility.
@@ -1061,7 +1072,7 @@ for Go source files:
A missing go directive line is assumed as `go 1.16`.
The effects of the directive line are __described here__.
- @__described here``https://go.dev/ref/mod#go-mod-file-go
+ ////__described here``https://go.dev/ref/mod#go-mod-file-go
If the Go language version of a Go source file is not specified by
any of the above ways, then the version of the used Go compiler is used.
@@ -1080,13 +1091,14 @@ The design causes two problems:
are not detected in time (due to insufficient testing, etc.),
then things may not go well.
- @__potential bugs `` https://github.com/golang/go/issues/66092
+ ////__potential bugs `` https://github.com/golang/go/issues/66092
Anyway, since Go 1.22, you should try to specify a Go language version for every Go source file,
in any of the above introduced ways, to avoid compiler version dependent behaviors.
This is the minimum standard to be a professional Go programmer in the Go 1.22+ era.
-### Upgrading module versions
+#::::::::::::::::::::::::::::::::::::::::::::::::::::
+ Upgrading module versions
If you are maintaining a public Go module which are depended by other Go projects,
please carefully check all the uses of `for;;` loops in the module's code
@@ -1099,7 +1111,8 @@ pay attention to those ones which language versions os
upgraded to Go 1.22 or higher
from a version with the old semantics before Go 1.22.
-### Avoid using freshly-declared loop variables in `for;;` loops
+#::::::::::::::::::::::::::::::::::::::::::::
+ Avoid using freshly-declared loop variables in `for;;` loops
if you worry about getting bitten by the pitful of the new semantics
Don't be too nervous, :D.
@@ -1107,22 +1120,22 @@ In fact, most `for;;` loops behave the same with either the old semantics or the
But if you're unsure about the new semantics,
you can always rewrite the following alike loop using the old semantics:
-```Go
+'''Go
for a, b, c := anExpression; aCondition; postStatement {
... // loop body
}
-```
+'''
as
-```Go
+'''Go
{
a, b, c := anExpression
for ; aCondition; postStatement {
... // loop body
}
}
-```
+'''
to avoid triggering the new semantics.
You can even specify which loop variables are instantiated
@@ -1131,7 +1144,7 @@ For example, in the following code, `a` and
`c` are instantiated per loop iteration, but `b` will be
only instantiated once during the whole loop.
-```Go
+'''Go
{
a, b, c := anExpression
for ; aCondition; postStatement {
@@ -1139,11 +1152,12 @@ only instantiated once during the whole loop.
... // loop body
}
}
-```
+'''
This is a little awkward, but it is much safer.
-## Final words
+#=====================================
+ Final words
Overall, I find the impact of the new semantics of `for-range` loops
is positive, while the impact of the new semantics of `for;;` loops is negative.
@@ -1160,7 +1174,7 @@ Depending on specific cases, such issues might be found in time or not.
In my honest opinion, the benefits of the new semantics of `for;;` loops
are rare and tiny, whereas the drawbacks are more prominent and serious.
-The semantic changes introduced in Go 1.22 significantly lower the threshold
+The semantic changes introduced in Go 1.22 significantly lower the standard
for maintaining backward compatibility. This is a bad start.
I have expressed my opinions in the following comments:
diff --git a/pages/fundamentals/evaluation-orders.html b/pages/fundamentals/evaluation-orders.html
index 0af5a456..8ea750b7 100644
--- a/pages/fundamentals/evaluation-orders.html
+++ b/pages/fundamentals/evaluation-orders.html
@@ -192,11 +192,11 @@
Initialization Order of Package-Level Variables
And please be aware of that some Go Toolchain versions don't correctly implement
-the abvoe rules described in the current section:
+the above rules described in the current section:
-a bug in Go Toolchain 1.22.n.
+a bug in Go Toolchain 1.22.0 - 1.22.4.
a bug in Go Toolchain 1.22-.
diff --git a/pages/fundamentals/keywords-and-identifiers.html b/pages/fundamentals/keywords-and-identifiers.html
index c1b97e74..0ba31fe8 100644
--- a/pages/fundamentals/keywords-and-identifiers.html
+++ b/pages/fundamentals/keywords-and-identifiers.html
@@ -53,19 +53,17 @@
Identifiers
An identifier is a token which must be composed of Unicode letters,
-Unicode digits (Number category Nd in Unicode Standard 8.0)
-and _ (underscore), and start with
+Unicode digits and _ (underscore), and start with
either an Unicode letter or _.
Here,
Unicode letters mean the characters defined in the Letter categories
Lu, Ll, Lt, Lm, or Lo
- of The Unicode Standard 8.0.
Unicode digits mean the characters defined
- in the Number category Nd of The Unicode Standard 8.0.
+ in the Number category Nd of The Unicode Standard.
-Golds also tries to fix some shortcomings of godoc and go doc, such as this, this and this.
-
+
+I didn't find a Go tool showing type implementation relations, so I decided to write one. During achieving this, I got many new ideas which form the tool to the final Golds design.
+
+I also have some different design opinions from the official godoc program developers, such as the manner of listing factory functions.
+
-
Is Golds recommended to run locally?
+
+Golds also tries to fix some shortcomings of godoc and go doc, such as this, this and this.
+
-
-Yes. But if you do want to serve your package docs on Internet,
-it is best to serve the generated HTML static doc pages to lower the server cost.
-
+
-
What are the requirements to run Golds?
+
-
-If a Go project needs cgo, then a proper C/C++ compiler is needed.
-
+
+Is Golds recommended to run locally?
+
-
-Some projects might need large memory capacity to analyze.
-For example, the recommended memory capacity to analyze the Kubernetes project is 8G+.
-However, 500M to 2G memory is okay for most Go projects.
-
+
+Yes. But if you do want to serve your package docs on Internet, it is best to serve the generated HTML static doc pages to lower the server cost.
+
+
+
+
+
+What are the requirements to run Golds?
+
+
+
+
+
+If a Go project needs cgo, then a proper C/Ccompiler is needed.
+
+Some projects might need large memory capacity to analyze. For example, the recommended memory capacity to analyze the Kubernetes project is 8G+. However, 500M to 2G memory is okay for most Go projects.
+
+
+
diff --git a/pages/apps-and-libs/golds.tmd b/pages/apps-and-libs/golds.tmd
new file mode 100644
index 00000000..19b7f05f
--- /dev/null
+++ b/pages/apps-and-libs/golds.tmd
@@ -0,0 +1,151 @@
+######################### Golds
+
+**%% Golds %%** is an experimental Go local docs server,
+a Go docs generator, and a Go code reader.
+
+- Demo: __docs and source code of standard packages__
+ (generated with `GOEXPERIMENT=arenas golds -gen -nouses -only-list-exporteds -render-doclinks -theme=dark std`).
+- Code is __hosted on Github__. Any feedback, including PR and bug reports, are welcome.
+- Please follow __@zigo_101__ to get the latest news of **%% Golds %%**
+ (and all kinds of Go details/facts/tips/etc.).
+
+ //// __docs and ... `` https://docs.go101.org/index.html
+ //// __hosted on Github `` https://github.com/go101/golds
+ //// __@zigo_101 `` https://twitter.com/zigo_101
+
+#======================= Features and Limitations
+
+Please read __the project home page__ for details.
+
+ //// __the project home page `` https://github.com/go101/golds
+
+#======================= Installation
+
+Run `go install go101.org/golds@latest` to install Golds.
+If the tool program name `golds` conflicts with another tool with the same name you are using,
+you can run any of the following commands to install **%% Golds %%** as a program with a different name:
+
+- Go docs generator: run `go install go101.org/golds/godoge@latest`
+- Go code reader: run `go install go101.org/golds/gocore@latest`
+- Go local docs (mainly for legacy. `gold` was the old default program name of **%% Golds %%**):
+ run `go install go101.org/golds/gold@latest`
+
+You may also clone this project firstly, then run the `go install` command
+in the respective program folders to install **%% Golds %%** as `golds`, `godoge`, or `gocore`.
+
+(NOTE: Go commands will install produced binaries into the Go binary installation path
+specified by the `GOBIN` environment variable, which defaults to the path of
+the `bin` subfolder under the first path specified in the `GOPATH` environment variable,
+which defaults to the path of the `go` subfolder under the path specified
+by the `HOME` environment variable.
+Please specify the Go binary installation path in the `PATH` environment variable
+to run **%% Golds %%** commands successfully.)
+
+#================================= Usages
+
+The main usage of **%% Golds %%** is to start a local docs server for a project,
+to either read package docs or study source code of the project. We can
+
+- run `golds .` to show docs of the package in the current directory
+ (and all its dependency packages).
+- run `golds ./...` to show docs of all the package under the current directory
+ (and all their dependency packages).
+- run `golds toolchain` (or `golds cmd`) to show docs of official toolchain packages.
+- run `golds std` to show docs of standard packages.
+ `std` can be mixed with any one of the above three arguments.
+- run `golds aPackage[/...][@aVersion]` to show docs of the specified packages
+ (and all their dependency packages).
+- run `golds foo.go bar.go baz.go` to show docs of the specified files
+ (and all their dependency packages).
+
+Each of the above commands will open a browser window automatically.
+We can use the `-s` or `-silent` options to turn off the behavior.
+
+The second usage of **%% Golds %%** is to generate static HTML doc pages for a project,
+with the `-gen` option:
+
+- `golds -gen -dir=generated -nouses .`
+- `golds -gen -dir=generated -nouses ./...`
+- `golds -gen -dir=generated -nouses std`
+
+The `-dir` option is optional and its default value is `.`(the working directory).
+The `-nouses` option used here is to generate docs with moderate sizes.
+
+The option `-source-code-reading` is used to control how to generate source code pages.
+Available values:
+
+- `plain`: generate simpler source code pages (no highlighting and no code navigations to reduce the total page size by 1/6 of the full docs size).
+- `highlight`: only highlight keywords and basic literals (no code navigations).
+- `rich`: rich code reading experience.
+- `external`: link to external code host websites (try its best, use `highlight` when failed).
+
+The option `-allow-network-connection` specifies whether or not network connections are allowed in determining external code host websites.
+
+Options to control generated docs sizes:
+
+- `-nouses`: don't generate identifier-uses pages (identifier-uses pages will occupy about 9/10 of the total page count and 2/3 of the full docs size).
+- `-source-code-reading=plain`
+- `-only-list-exporteds`: don't list unexported package-level code elements in package-details pages.
+- `-compact` is a shortcut of the combination of the above compact docs generation options.
+- Using `-source-code-reading=external` along with `-compact` will further reduce the generated docs size.
+
+The size of the docs generated by `golds -gen -compact ./...` is about 1/6 of `golds -gen ./...` and about 1/2 of `golds -gen -nouses ./...`. The size of the docs generated by `golds -gen -compact -source-code-reading=external ./...` is about 1/6 of `golds -gen -compact ./...`.
+
+The `-wdpkgs-listing` option is used to specify how to list the packages in the working directory. Available values include
+
+- `general` (the default, list them with others by alphabetical order)
+- `promoted` (list them before others)
+- `solo` (list them without others)
+
+The `-render-doclinks` option is used to control whether or not to render links in docs.
+
+The `-theme` option is used to control page styling
+ Supported values include `auto` (default), `light` and `dark`.
+ The `auto` value is equivalent to `light` plus custom styling set
+ in the `UserConfigDir/golds/custom.css` file (if it exists).
+
+The third usage of **%% Golds %%** is to serve files within a directory
+("Golds" also means Go local directory server).
+For example, we can run `golds -dir=.` (or simply `golds`) from the HTML docs generation directory to view the generated docs in browser. The `-s` and `-silent` options also work in this mode.
+
+The `golds` command recognizes the `GOOS` and `GOARCH` environment variables.
+
+#================================ FAQ
+
+#? What does **%% Golds %%** mean?
+
+"Golds" is an abbreviation of **Go** **l**ocal **d**ocs **s**erver.
+It also means **Go** **l**ocal **d**irectory **s**erver.
+
+#? Why **%% Golds %%**?
+
+{
+I didn't find a Go tool showing type implementation relations,
+so I decided to write one. During achieving this,
+I got many new ideas which form the tool to the final **%% Golds %%** design.
+
+I also have some different design opinions from the official `godoc` program developers,
+such as __the manner of listing factory functions__.
+
+ //// __the manner... `` https://github.com/golang/go/issues/28006
+
+Golds also tries to fix some shortcomings of `godoc` and `go doc`,
+such as __this``https://github.com/golang/go/issues/6600__,
+__this``https://github.com/golang/go/issues/40360__
+and __this``https://github.com/golang/go/issues/5860__.
+}
+
+#? Is **%% Golds %%** recommended to run locally?
+
+Yes. But if you do want to serve your package docs on Internet,
+it is best to serve the generated HTML static doc pages to lower the server cost.
+
+#? What are the requirements to run **%% Golds %%**?
+
+{
+If a Go project needs cgo, then a proper C/C++ compiler is needed.
+
+Some projects might need large memory capacity to analyze.
+For example, the recommended memory capacity to analyze the Kubernetes project is 8G+.
+However, 500M to 2G memory is okay for most Go projects.
+}
diff --git a/pages/apps-and-libs/gotv.html b/pages/apps-and-libs/gotv.html
index b6e2ac2e..913e4b0c 100644
--- a/pages/apps-and-libs/gotv.html
+++ b/pages/apps-and-libs/gotv.html
@@ -1,58 +1,82 @@
-
-
GoTV
-
GoTV is an abbreviation of GoToolchain Version.
-It is a tool used to manage and use multiple coexisting installations of official Go toolchain versions harmoniously and conveniently.
+
+GoTV is a tool used to manage and use multiple coexisting installations of official Go toolchain versions harmoniously and conveniently. The name is an abbreviation of GoToolchain Version.
+
+Project page: https://github.com/go101/gotv
+
+Please follow @zigo_101 to get the latest news of GoTV (and all kinds of Go details/facts/tips/...).
+
-
Please follow @zigo_101 to get the latest news of GoTV
-(and all kinds of Go details/facts/tips/...).
+
Installation
-
Run
+
+Run
+
+
go install go101.org/gotv@latest
+
-
go install go101.org/gotv@latest
-
+
+to install GoTV.
+
+A 1.17+ toolchain version is needed to finish the installation. The toolchain version may be uninstalled after pinning a suitable toolchain version (see below).
+
-
to install GoTV.
+
Usage
-
A 1.17+ toolchain version is needed to finish the installation.
-The toolchain version may be uninstalled after pinning a suitable toolchain version (see below).
+
+Run gotv without any arguments to show help messages.
+
+Most gotv commands are in the following format:
+
+
gotv ToolchainVersion [go-arguments...]
+
-
Usage
+
+During running the first such a command, the Go git repository will be cloned (which needs several minutes to finish).
+
+ToolchainVersion might be
+
-
Run gotv without any arguments to show help messages.
+
-
Most gotv commands are in the following format:
+
+a Go release version, such as 1.17.13, 1.18, 1.19rc1, which mean the release tags go1.17.13, go1.18, go1.19rc1, respectively, in the Go git repository. Note:
+
-
gotv ToolchainVersion [go-arguments...]
-
+
+1.N. means the latest release of 1.N.
+
-
During running the first such a command, the Go git repository will be cloned (which needs several minutes to finish).
+
+1. means the latest Go 1 release version.
+
-
ToolchainVersion might be
+
+. means the latest Go release version.
+
-
-
a Go release version, such as 1.17.13, 1.18, 1.19rc1,
-which mean the release tags go1.17.13, go1.18, go1.19rc1, respectively,
-in the Go git repository.
-Note:
+
+:1.N, which means the local latest release-branch.go1.N branch in the Go git repository.
+
-
-
1.N. means the latest release of 1.N.
-
1. means the latest Go 1 release version.
-
. means the latest Go release version.
-
-
:1.N, which means the local latest release-branch.go1.N branch in the Go git repository.
-
:tip, which means the local latest master branch in the Go git repository.
-
a version suffixed with ! means to fetch remote versions (by running gotv fetch-versions) firstly.
-
+
+:tip, which means the local latest master branch in the Go git repository.
+
-
Examples:
+
+a version suffixed with ! means to fetch remote versions (by running gotv fetch-versions) firstly.
+
-
$ gotv 1.17. version
+
+
+
+Examples:
+
+
$ gotv 1.17. version
[Run]: $HOME/.cache/gotv/tag_go1.17.13/bin/go version
go version go1.17.13 linux/amd64
@@ -63,16 +87,16 @@
Usage
$ cat search.go
package main
-import "fmt"
+import "fmt"
func demoFilter(n int) bool {
- return n & 1 == 0;
+ return n & 1 == 0;
}
// Search values and return them without perverting order.
func search(start, end int)(r []int) {
var count = 0
- for i, index := start, 0; i <= end; i++ {
+ for i, index := start, 0; i <= end; i++ {
if demoFilter(i) {
count++
defer func(value int) {
@@ -96,14 +120,16 @@
Usage
$ gotv 1.22. run search.go
[Run]: $HOME/.cache/gotv/tag_go1.22.1/bin/go run search.go
[0 0 0 0 0]
-
+
-
(The example code comes from this blog article.
-More uses of GoTV are demonstrated here.)
+
+(The example code comes from this blog article. More uses of GoTV are demonstrated here.)
+
-
All gotv specific commands:
-
-
# sync the local Go git repository with remote
+
+All gotv specific commands:
+
+
# sync the local Go git repository with remote
gotv fetch-versions
# list all versions seen locally
@@ -126,52 +152,73 @@
Usage
# check the default toolchain version (since v0.2.1)
gotv default-version
-
+
-
Pin a toolchain version
+
-
We can use the gotv pin-version command to pin a specific toolchain version to a stable path.
-After adding the stable path to the PATH environment veriable,
-we can use the official go command directly.
-And after doing these, the toolchain versions installed through ways other than GoTV
-may be safely uninstalled.
+
Pin a toolchain version
-
It is recommended to pin a 1.17+ version for bootstrap purpose now.
-The following example shows how to pin Go toolchain version 1.17.13:
+
+We can use the gotv pin-version command to pin a specific toolchain version to a stable path. After adding the stable path to the PATH environment veriable, we can use the official go command directly. And after doing these, the toolchain versions installed through ways other than GoTV may be safely uninstalled.
+
+It is recommended to pin a 1.17+ version for bootstrap purpose now. The following example shows how to pin Go toolchain version 1.17.13:
+
-
$ gotv pin-version 1.17.
+
+
$ gotv pin-version 1.17.
[Run]: cp -r $HOME/.cache/gotv/tag_go1.17.13 $HOME/.cache/gotv/pinned-toolchain
Please put the following shown pinned toolchain path in
your PATH environment variable to use go commands directly:
/home/username/.cache/gotv/pinned-toolchain/bin
-
-
-
After the prompted path is added to the PATH environment variable,
-open a new terminal window:
+
-
$ go version
+
+After the prompted path is added to the PATH environment variable, open a new terminal window:
+
+
$ go version
go version go1.17.13 linux/amd64
-
+
-
The command gotv pin-version .! will upgrade the pinned toolchain to the latest release version
-(which may be a beta or rc version).
+
+The command gotv pin-version .! will upgrade the pinned toolchain to the latest release version (which may be a beta or rc version).
+
Set a bootstrap toolchain version
-
To build a toolchain verision, another already built toolchain version is needed to be used in the building process.
-The other toolchain version is called the bootstrap version.
+
+To build a toolchain verision, another already built toolchain version is needed to be used in the building process. The other toolchain version is called the bootstrap version.
+
+Some facts:
+
+
+
+
+
+Toolchain versions <= 1.12.17 are unable to be built with toochain versions >= 1.16;
+
-
Some facts:
+
+Toolchain versions <= 1.5.4 are uanable to be built with toolchain versions >= 1.6;
+
Currently, GoTV uses the toolchain set in the PATH environment variable as the bootstrap version by default.
-If GOROOT_BOOTSTRAP environment variable is set, then its value will be used.
+
+
+
+Currently, GoTV uses the toolchain set in the PATH environment variable as the bootstrap version by default. If GOROOT_BOOTSTRAP environment variable is set, then its value will be used.
+
diff --git a/pages/apps-and-libs/gotv.md b/pages/apps-and-libs/gotv.tmd
similarity index 72%
rename from pages/apps-and-libs/gotv.md
rename to pages/apps-and-libs/gotv.tmd
index 362f5432..e7d7e745 100644
--- a/pages/apps-and-libs/gotv.md
+++ b/pages/apps-and-libs/gotv.tmd
@@ -1,36 +1,39 @@
-# GoTV
+################################## GoTV
-**GoTV** is an abbreviation of **Go** **T**oolchain **V**ersion.
-It is a tool used to manage and use multiple coexisting installations of official Go toolchain versions harmoniously and conveniently.
+**%% GoTV %%** is a tool used to manage and use multiple coexisting installations
+of official Go toolchain versions harmoniously and conveniently.
+The name is an abbreviation of **Go** **T**oolchain **V**ersion.
-Project page: https://github.com/go101/gotv
+Project page: __https://github.com/go101/gotv__
-Please follow [@zigo_101](https://twitter.com/zigo_101) to get the latest news of GoTV
+Please follow __@zigo_101__ to get the latest news of GoTV
(and all kinds of Go details/facts/tips/...).
-## Installation
+ //// __@zigo_101 `` https://twitter.com/zigo_101
+
+#================================ Installation
Run
-```
+'''
go install go101.org/gotv@latest
-```
+'''
-to install GoTV.
+to install **%% GoTV %%**.
A 1.17+ toolchain version is needed to finish the installation.
The toolchain version may be uninstalled after pinning a suitable toolchain version (see below).
-## Usage
+#============================== Usage
Run `gotv` without any arguments to show help messages.
Most `gotv` commands are in the following format:
-```
+'''
gotv ToolchainVersion [go-arguments...]
-```
+'''
During running the first such a command, the Go git repository will be cloned (which needs several minutes to finish).
@@ -49,7 +52,7 @@ During running the first such a command, the Go git repository will be cloned (w
Examples:
-```
+'''
$ gotv 1.17. version
[Run]: $HOME/.cache/gotv/tag_go1.17.13/bin/go version
go version go1.17.13 linux/amd64
@@ -94,14 +97,17 @@ $ gotv 1.21. run search.go
$ gotv 1.22. run search.go
[Run]: $HOME/.cache/gotv/tag_go1.22.1/bin/go run search.go
[0 0 0 0 0]
-```
+'''
+
+%% (The example code comes from __this blog article__.
+More uses of GoTV are demonstrated __here__.)
-_(The example code comes from [this blog article](https://go101.org/blog/2024-03-01-for-loop-semantic-changes-in-go-1.22.html).
-More uses of GoTV are demonstrated [here](https://go101.org/blog/2022-08-22-some-undocumented-changes-in-go-1.18-and-1.19.html).)_
+ //// __this blog article `` https://go101.org/blog/2024-03-01-for-loop-semantic-changes-in-go-1.22.html
+ //// __here `` https://go101.org/blog/2022-08-22-some-undocumented-changes-in-go-1.18-and-1.19.html
All `gotv` specific commands:
-```
+'''
# sync the local Go git repository with remote
gotv fetch-versions
@@ -125,9 +131,9 @@ gotv default-version ToolchainVersion
# check the default toolchain version (since v0.2.1)
gotv default-version
-```
+'''
-## Pin a toolchain version
+#=============================== Pin a toolchain version
We can use the `gotv pin-version` command to pin a specific toolchain version to a stable path.
After adding the stable path to the `PATH` environment veriable,
@@ -135,10 +141,12 @@ we can use the official `go` command directly.
And after doing these, the toolchain versions installed through ways other than GoTV
may be safely uninstalled.
-It is recommended to pin a 1.17+ version for [bootstrap purpose](https://github.com/golang/go/issues/44505) now.
+It is recommended to pin a 1.17+ version for __bootstrap purpose__ now.
The following example shows how to pin Go toolchain version 1.17.13:
-```
+ //// __bootstrap purpose `` https://github.com/golang/go/issues/44505
+
+'''
$ gotv pin-version 1.17.
[Run]: cp -r $HOME/.cache/gotv/tag_go1.17.13 $HOME/.cache/gotv/pinned-toolchain
@@ -146,20 +154,20 @@ Please put the following shown pinned toolchain path in
your PATH environment variable to use go commands directly:
/home/username/.cache/gotv/pinned-toolchain/bin
-```
+'''
After the prompted path is added to the PATH environment variable,
open a new terminal window:
-```
+'''
$ go version
go version go1.17.13 linux/amd64
-```
+'''
The command `gotv pin-version .!` will upgrade the pinned toolchain to the latest release version
(which may be a beta or rc version).
-## Set a bootstrap toolchain version
+#================================= Set a bootstrap toolchain version
To build a toolchain verision, another already built toolchain version is needed to be used in the building process.
The other toolchain version is called the bootstrap version.
@@ -168,9 +176,13 @@ Some facts:
* Toolchain versions <= 1.12.17 are unable to be built with toochain versions >= 1.16;
* Toolchain versions <= 1.5.4 are uanable to be built with toolchain versions >= 1.6;
-* [A 1.17.13+ toolchain version is required to build 1.20+ toolchain versions](https://github.com/golang/go/issues/44505);
-* [A 1.20+ toolchain version is required to build 1.22+ toolchain versions](https://github.com/golang/go/issues/54265).
-* It is proposed to [require a 1.22+ toolchain version to build 1.24+ toolchain versions](https://github.com/golang/go/issues/64751).
+* __A 1.17.13+ toolchain version is required to build 1.20+ toolchain versions__;
+* __A 1.20+ toolchain version is required to build 1.22+ toolchain versions__.
+* It is proposed to __require a 1.22+ toolchain version to build 1.24+ toolchain versions__.
+
+ //// __A 1.17.13+ ... `` https://github.com/golang/go/issues/44505
+ //// __A 1.20+ ... `` https://github.com/golang/go/issues/54265
+ //// __... 1.24+ toolchain versions `` https://github.com/golang/go/issues/64751
Currently, GoTV uses the toolchain set in the `PATH` environment variable as the bootstrap version by default.
If `GOROOT_BOOTSTRAP` environment variable is set, then its value will be used.
From eae17c39fa7d5eb7a14857c396bf0b7b4ef481d6 Mon Sep 17 00:00:00 2001
From: Go101 <22589241+go101@users.noreply.github.com>
Date: Fri, 7 Jun 2024 22:40:33 +0800
Subject: [PATCH 3/3] fix a blog tmd
---
...4-03-01-for-loop-semantic-changes-in-go-1.22.html | 12 +++++++++---
...24-03-01-for-loop-semantic-changes-in-go-1.22.tmd | 6 +++---
2 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/pages/blog/2024-03-01-for-loop-semantic-changes-in-go-1.22.html b/pages/blog/2024-03-01-for-loop-semantic-changes-in-go-1.22.html
index 8d97bd7c..0b138d39 100644
--- a/pages/blog/2024-03-01-for-loop-semantic-changes-in-go-1.22.html
+++ b/pages/blog/2024-03-01-for-loop-semantic-changes-in-go-1.22.html
@@ -428,7 +428,9 @@
Be careful when capturing loop variables in closures
-#:::::::::::::::::::::::::::::::::::::::::::::::::; Be careful when taking addresses of loop variables
+
+
Be careful when taking addresses of loop variables
+
Similarly, since Go 1.22, it may be dangerous to use the address of a freshly-declared loop variable across loop iterations.
@@ -510,7 +512,9 @@
Be careful when capturing loop variables in closures
-#::::::::::::::::::::::::::::::::::::::::::::::::; Be careful when moving the 3rd clause statements inside loop bodies
+
+
Be careful when moving the 3rd clause statements inside loop bodies
+
Since Go 1.22, the following two loops might be not equivalent with each other any more (prior to Go 1.22, they are equivalent).
@@ -565,7 +569,9 @@
Be careful when capturing loop variables in closures
-#::::::::::::::::::::::::::::::::::::::::::::::::::; Be careful when declaring no-copy values as loop variables
+
+
Be careful when declaring no-copy values as loop variables
+
As explained above, since Go 1.22, at the start of each loop iteration, each freshly-declared loop variable will get copied once, implicitly. The implication means that, since Go 1.22, it is not a good idea to declare no-copy values as loop variables, such as sync.Mutex, sync/atomic.Int64, bytes.Buffer, and strings.Builder values etc.
diff --git a/pages/blog/2024-03-01-for-loop-semantic-changes-in-go-1.22.tmd b/pages/blog/2024-03-01-for-loop-semantic-changes-in-go-1.22.tmd
index ec5438fb..00400fdd 100644
--- a/pages/blog/2024-03-01-for-loop-semantic-changes-in-go-1.22.tmd
+++ b/pages/blog/2024-03-01-for-loop-semantic-changes-in-go-1.22.tmd
@@ -464,7 +464,7 @@ since Go 1.22, a freshly-declared loop variable may have many instances at run t
whether or not it is modified in `postStatement`.
Each of the instances is instantiated in one iteration.
-#:::::::::::::::::::::::::::::::::::::::::::::::::;
+#::::::::::::::::::::::::::::::::::::::::::::::::
Be careful when taking addresses of loop variables
Similarly, since Go 1.22, it may be dangerous to use
@@ -557,7 +557,7 @@ $ gotv 1.22. run demo-pointer2.go
...
'''
-#::::::::::::::::::::::::::::::::::::::::::::::::;
+#::::::::::::::::::::::::::::::::::::::::::::::::
Be careful when moving the 3rd clause statements inside loop bodies
Since Go 1.22, the following two loops might be not equivalent with each other any more
@@ -616,7 +616,7 @@ true
2
'''
-#::::::::::::::::::::::::::::::::::::::::::::::::::;
+#::::::::::::::::::::::::::::::::::::::::::::::::::
Be careful when declaring no-copy values as loop variables
As explained above, since Go 1.22, at the start of each loop iteration,