Skip to content

Commit

Permalink
fmt: print &map like &slice and &struct
Browse files Browse the repository at this point in the history
It was inconsistent.
Also test these better.
Also document the default format for types.
This wasn't written down.

Fixes golang#8470.

LGTM=iant
R=golang-codereviews, iant
CC=golang-codereviews
https://golang.org/cl/154870043
  • Loading branch information
robpike authored and wheatman committed Jul 23, 2018
1 parent 885fed7 commit bc14e8d
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 3 deletions.
17 changes: 16 additions & 1 deletion src/fmt/doc.go
Expand Up @@ -13,7 +13,7 @@
The verbs:
General:
%v the value in a default format.
%v the value in a default format
when printing structs, the plus flag (%+v) adds field names
%#v a Go-syntax representation of the value
%T a Go-syntax representation of the type of the value
Expand Down Expand Up @@ -51,6 +51,21 @@
There is no 'u' flag. Integers are printed unsigned if they have unsigned type.
Similarly, there is no need to specify the size of the operand (int8, int64).
The default format for %v is:
bool: %t
int, int8 etc.: %d
uint, uint8 etc.: %d, %x if printed with %#v
float32, complex64, etc: %g
string: %s
chan: %p
pointer: %p
For compound objects, the elements are printed using these rules, recursively,
laid out like this:
struct: {field0 field1 ...}
array, slice: [elem0 elem1 ...]
maps: map[key1:value1 key2:value2]
pointer to above: &{}, &[], &map[]
Width is specified by an optional decimal number immediately following the verb.
If absent, the width is whatever is necessary to represent the value.
Precision is specified after the (optional) width by a period followed by a
Expand Down
34 changes: 32 additions & 2 deletions src/fmt/fmt_test.go
Expand Up @@ -965,11 +965,12 @@ func TestFlagParser(t *testing.T) {
}

func TestStructPrinter(t *testing.T) {
var s struct {
type T struct {
a string
b string
c int
}
var s T
s.a = "abc"
s.b = "def"
s.c = 123
Expand All @@ -979,15 +980,38 @@ func TestStructPrinter(t *testing.T) {
}{
{"%v", "{abc def 123}"},
{"%+v", "{a:abc b:def c:123}"},
{"%#v", `fmt_test.T{a:"abc", b:"def", c:123}`},
}
for _, tt := range tests {
out := Sprintf(tt.fmt, s)
if out != tt.out {
t.Errorf("Sprintf(%q, &s) = %q, want %q", tt.fmt, out, tt.out)
t.Errorf("Sprintf(%q, s) = %#q, want %#q", tt.fmt, out, tt.out)
}
// The same but with a pointer.
out = Sprintf(tt.fmt, &s)
if out != "&"+tt.out {
t.Errorf("Sprintf(%q, &s) = %#q, want %#q", tt.fmt, out, "&"+tt.out)
}
}
}

func TestSlicePrinter(t *testing.T) {
slice := []int{}
s := Sprint(slice)
if s != "[]" {
t.Errorf("empty slice printed as %q not %q", s, "[]")
}
slice = []int{1, 2, 3}
s = Sprint(slice)
if s != "[1 2 3]" {
t.Errorf("slice: got %q expected %q", s, "[1 2 3]")
}
s = Sprint(&slice)
if s != "&[1 2 3]" {
t.Errorf("&slice: got %q expected %q", s, "&[1 2 3]")
}
}

// presentInMap checks map printing using substrings so we don't depend on the
// print order.
func presentInMap(s string, a []string, t *testing.T) {
Expand All @@ -1014,6 +1038,12 @@ func TestMapPrinter(t *testing.T) {
a := []string{"1:one", "2:two", "3:three"}
presentInMap(Sprintf("%v", m1), a, t)
presentInMap(Sprint(m1), a, t)
// Pointer to map prints the same but with initial &.
if !strings.HasPrefix(Sprint(&m1), "&") {
t.Errorf("no initial & for address of map")
}
presentInMap(Sprintf("%v", &m1), a, t)
presentInMap(Sprint(&m1), a, t)
}

func TestEmptyMap(t *testing.T) {
Expand Down
4 changes: 4 additions & 0 deletions src/fmt/print.go
Expand Up @@ -994,6 +994,10 @@ BigSwitch:
p.buf.WriteByte('&')
p.printValue(a, verb, depth+1)
break BigSwitch
case reflect.Map:
p.buf.WriteByte('&')
p.printValue(a, verb, depth+1)
break BigSwitch
}
}
fallthrough
Expand Down

0 comments on commit bc14e8d

Please sign in to comment.