关于 Golang 自带的 println 以及 print 函数,在这里有相应的说明:

https://go.dev/ref/spec#Bootstrapping

Current implementations provide several built-in functions useful during bootstrapping. These functions are documented for completeness but are not guaranteed to stay in the language. They do not return a result.

1
2
3
4
Function   Behavior

print      prints all arguments; formatting of arguments is implementation-specific
println    like print but prints spaces between arguments and a newline at the end

Implementation restriction: print and println need not accept arbitrary argument types, but printing of boolean, numeric, and string types must be supported.

按照其说明,这些内建函数保证布尔量、数字、以及字符串的正确输出,而对于其他的类型则较为随心所欲。

在这里有一些相关的讨论:

https://stackoverflow.com/questions/14680255/difference-between-fmt-println-and-println-in-go

我们给出的例子是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main_test

import (
	"fmt"
	"io"
	"testing"

	"gopkg.in/hedzr/errors.v3"
)

func TestPrintln(t *testing.T) {
	var err = errors.New("OK").WithErrors(io.EOF)

	println(err)
	println(io.EOF)

	fmt.Println(err)
	fmt.Println(io.EOF)

	print("An old falcon\n")
	println("An old falcon")

	vararg(56, 7)  // model prints
	vararg2(56, 7) // primitives directly

	// fmt.Printf("%+v", err) // prints with stacktrace info
}

func vararg(args ...any) {
	for _, v := range args {
		println(v)
	}
}

func vararg2(args ...int) {
	for _, v := range args {
		println(v)
	}
}

它的输出应该类似于这样:

1
2
3
4
5
6
7
8
(0x1158998,0xc00002a180)
(0x1157760,0x120ce50)
An old falcon
An old falcon
(0x11076c0,0x1156e20)
(0x11076c0,0x1156e28)
56
7

原因就没什么好解释了。

数据的解读,尤其是复杂类型,例如第一行的对应于 error 对象的输出内容的含义是这样的:

两个数字构成了一个 pair。前一个数字代表着资源编号,后一个数字代表着对象的内存地址。

所谓资源编号,是我的一个随机制造的词语。对于 eface 数据来说,这个数字是 eface._type 的 hex 表达;而对于 iface 来说,它是 iface.tab 的 hex 表达。由于这个话题涉及到 golang 源码中的内部数据结构问题,所以深究其意义在本文中是没意义的——你只需要知道这是个内部使用的资源编号就够了。

所以 println 函数的输出带有很大的草率性,它不适合于被用在 dump 复杂类型对象的可阅读文字值的场所,而是适于内部开发人员调试而用。

对于绝大多数常规性的开发来说,你应该使用 fmt.Printf 或者各种基于其上或者 fmt.Fprintf 的变种包装。大多数 logger 库都是如此。

REFs

如果有兴趣研究 Golang 源码,print/println 的实现部分在这里:

https://github.com/golang/go/blob/a1e9148e3dbb20a18e0139583e7d835cc7a820bf/src/runtime/print.go

🔚

标签:

分类:

更新时间:

留下评论