The Format function formats strings with Python-like placeholder syntax,
writing into a caller-supplied buffer. Common formatting paths are designed to
reuse that buffer without allocating; see Performance for the
cases where allocations may still occur.
func Format(dst []byte, template string, a ...any) []byte// Basic usage
buf := make([]byte, 0, 128)
buf = format.Format(buf, "Hello {}, you are {} years old!", "Alice", 30)
// Result: "Hello Alice, you are 30 years old!"
// Reuse the buffer
buf = format.Format(buf[:0], "Price: ${:.2f}", 19.99)
// Result: "Price: $19.99"
// Various format specifiers
buf = format.Format(nil, "{:#08x} {:,d}", 255, 1000000)
// Result: "0x0000ff 1,000,000"- Integers:
int,int8,int16,int32,int64 - Unsigned integers:
uint,uint8,uint16,uint32,uint64,uintptr - Floats:
float32,float64 - Strings:
string - Byte slices and arrays:
[]byte(text by default),[N]byte(hex by default), both with the presentations documented below - Booleans:
bool - Durations:
time.Duration
A defined type is formatted by its underlying kind, so type ID uint64 obeys
every uint64 spec and {:x} on ID(255) gives ff. An Error or String
method takes precedence over the kind, which is why time.Duration prints as
1.5s and not as an integer.
Types implementing error or Stringer are formatted as strings, using the
same string formatting rules — so {:>10} aligns an error the way it aligns any
other string. A type with both methods is rendered by Error(), the order fmt
uses, and a nil pointer held in either interface prints as <nil> rather than
panicking.
Types with no supported kind — structs, maps, non-byte slices — fall back to
fmt.Sprintf("%v", value), and with spec {#}/{:#} to
fmt.Sprintf("%#v", value). These ignore the rest of the spec.
{}- empty placeholder (uses default formatting){format}or{:format}- placeholder with format spec (:prefix is optional){{- literal{}}- literal}
Formatting is a single streaming pass over the template: literal runs are copied straight through and each placeholder is formatted where it stands, so there is no parse table and no limit on the number of placeholders. Given a pre-allocated buffer, common built-in values and format specs normally need no internal allocations.
Passing an argument to a ...any parameter boxes it and may move that box to the
heap. In the benchmark call shape below, each runtime argument costs one
allocation, while values the compiler can keep in static interface values do
not. Exact escape behaviour is compiler- and call-site-dependent:
The numbers are from one local benchmark run and vary with the compiler and hardware.
| ns/op | B/op | allocs/op | |
|---|---|---|---|
format.Format, 3 runtime arguments |
248 | 32 | 3 |
fmt.Appendf, the same |
257 | 32 | 3 |
format.Format, 3 constants |
180 | 0 | 0 |
direct Append*, 3 values |
135 | 0 | 0 |
The package does not use unsafe to influence escape analysis because a user's
String or Error method may retain its receiver. Unsupported types also use
fmt.Sprintf, user methods may allocate themselves, and unusually large float
formats may outgrow the internal scratch buffer.
Where the allocation matters, use the direct append API.
It takes concrete types and avoids any boxing. With enough destination
capacity, common formats stay allocation-free and are faster in the benchmark
above.
Important notes:
- The
format_speccan optionally start with a colon:(e.g., both{>10}and{:>10}work). - A
[]byteargument must not refer todst's backing array, even whendsthas zero length. Calls such asFormat(b[:0], "{x}", b)are unsupported. - A spec that cannot be parsed is reported inline as
%!(BADSPEC:<spec>)followed by the value formatted plainly, so the mistake is visible without the data being lost. The usual cause is a spec meeting the wrong argument type —{:.2f}given anint, or{:,d}given afloat64. - Width is limited to 1000, float precision to 340 (enough for any
float64:math.MaxFloat64has 309 integer digits), and string precision to 16,777,216 runes. A spec exceeding a bound is treated as unparsable and the value is formatted without it.
When the value and its format spec are known at the call site, these functions
skip the template parser and the any boxing entirely. They take the same
format_spec strings documented below, without the surrounding braces.
func AppendInt(dst []byte, formatSpec string, v int64) []byte
func AppendUint(dst []byte, formatSpec string, v uint64) []byte
func AppendFloat(dst []byte, formatSpec string, v float64) []byte
func AppendFloat32(dst []byte, formatSpec string, v float32) []byte
func AppendDuration(dst []byte, formatSpec string, v time.Duration) []byte
func AppendString(dst []byte, formatSpec string, s string) []byte
func AppendBytes(dst []byte, formatSpec string, b []byte) []byte
func AppendSigFixed(dst []byte, value float64) []byte
func AppendSigFixed32(dst []byte, value float32, sig int) []byte
func AppendSigFixed64(dst []byte, value float64, sig int) []bytebuf = format.AppendUint(buf, ",d", 18446744073709551615) // "18,446,744,073,709,551,615"
buf = format.AppendFloat(buf, ".2f", 19.99) // "19.99"
buf = format.AppendDuration(buf, ">8", 1500*time.Millisecond) // " 1.5s"
buf = format.AppendString(buf, ">8", "αβγδεζ") // " αβγδεζ"
buf = format.AppendBytes(buf, "0>8X", []byte{1, 2}) // "00000102"AppendBytes requires b not to refer to dst's backing array, even when
dst has zero length. Calls such as AppendBytes(b[:0], spec, b) are
unsupported.
An unparsable formatSpec yields %!(BADSPEC:<spec>) followed by the value in
plain strconv-style output (base 10 for integers, 'g' with round-trip
precision for floats).
- Fill: A character used to pad the string to meet the width (default is space
' '). - Align: Controls how the string is positioned within the width:
<: Left-aligned (default for strings).>: Right-aligned.^: Center-aligned.=: Not applicable to strings (used for numbers only; excluded here).
- Width: Minimum field width (an integer ≥ 0). If the string is shorter, it's padded; if longer, it's unchanged unless precision truncates it.
- Precision: Maximum number of characters to display (
.Nwhere N is an integer ≥ 0). Truncates the string if longer.
format_spec ::= [[fill] align] [width] ["." precision]
| [[fill] align] "." precision
| "." precision
fill ::= <any character except '{', '}', '<', '>', or '^'>
align ::= "<" | ">" | "^"
width ::= digit+
digit ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
precision ::= digit+With an empty spec, []byte is appended as text. String alignment, width and
precision specs such as {>10} and {.3} use the same rules as string.
A byte array such as [32]byte accepts the same specs, but its default
presentation is x, not text: an array is a hash, key or id far more often
than it is text, raw bytes would put control characters into a log line, and hex
is the only form usable outside one. This covers every spec that names no
presentation, so {} and {>10} both give hex and text cannot slip back in
through a bare layout. {v} still prints the decimal list %v gives.
An unparsable spec falls back to the default presentation as well, so
%!(BADSPEC:...) is followed by hex for an array and by the raw bytes for a
[]byte.
Text for an array is therefore reachable only by slicing it — h[:] is the
explicit way to say the bytes are a stream — so {} on h and on h[:]
deliberately differ.
An array held in an interface cannot be sliced, so it is copied first — without allocating, up to 64 bytes.
The following specs select a non-text presentation:
| Spec | Meaning | Example for []byte("Hi\n") |
|---|---|---|
x |
Lowercase hexadecimal | 48690a |
X |
Uppercase hexadecimal | 48690A |
q |
Quoted and escaped text | "Hi\n" |
v |
Decimal byte list | [72 105 10] |
# |
Go syntax, naming the argument's own type | []byte{0x48, 0x69, 0xa} |
The x, X and q presentations accept a string fill, alignment and width
prefix, for example {>12x}, {0>12X} and {^16q}. Width applies to the
rendered representation, including quotes and escapes for q. Precision is not
accepted for these presentations. The v and # specs remain standalone.
byte_format_spec ::= [[fill] align] [width] ("x" | "X" | "q")
| "v"
| "#"
| format_specA defined byte-slice type uses the same rules unless its Error or String
method takes precedence.
- Fill – any character (except
{or}) used for padding. Default: space' '. - Align – positioning inside the field width:
<– left-aligned>– right-aligned (default for numbers when no sign-aware option is used)^– center-aligned=– forces the sign/prefix to the leftmost position and pads after the sign (very useful with+, space, or0x/0bprefixes)
- Sign – controls display of the sign:
+– always show sign (+123,-123)-– sign only for negative numbers (default)- (space) – positive numbers get a leading space (
123,-123)
- Alternate form (
#) – adds the base prefix:- binary →
0b… - octal →
0o… - hex →
0x…/0X…
- binary →
- Zero-padding (
0) – shorthand forfill='0'+align='>'(oralign='='with sign). Overridden by explicit fill/align. - Width – minimum field width (integer ≥ 0)
- Grouping option (
,) – use commas as thousands separators (also_for underscores) - Type – optional for integers; defaults to
dand determines the presentation:
| Type | Meaning | Example (1234) |
|---|---|---|
d |
Decimal integer (default) | 1234 |
b |
Binary | 10011010010 0b10011010010 (with #) |
o |
Octal | 2322 0o2322 (with #) |
x |
Hexadecimal lowercase | 4d2 0x4d2 (with #) |
X |
Hexadecimal uppercase | 4D2 0X4D2 (with #) |
c |
Unicode character (int → chr) | 65 → 'A' |
h |
produces a human readable representation of an SI size. | 82854982 -> 83 MB |
A value formatted with c that is not a valid Unicode code point falls back to
its plain base-10 representation. Fill, alignment, sign, alternate form,
zero-padding, width and grouping are not applied to this fallback.
format_spec ::= [[fill] align] [sign] ["#"] ["0"] [width] [grouping_option] [type]
fill ::= <any character except "{", "}", "<", ">", "^">
align ::= "<" | ">" | "^" | "="
sign ::= "+" | "-" | " "
grouping_option ::= "," | "_"
width ::= digit+
digit ::= "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
type ::= "b" | "c" | "d" | "h" | "o" | "x" | "X"
; note: "d" is the most common-
Fill – Any character used for padding (default: space). Can only be specified together with an align.
-
Align – Positioning inside the field width:
<left>right (default for numbers, as in Python)^center=forces the sign to the leftmost position (only useful with a fill character and a width)
-
Sign – Controls display of the sign:
- (none) show sign only for negative numbers (default)
+always show sign for positive and negative(space) show space for positive, minus for negative
-
Alternate form (#) – forces a decimal point where there would be none, and for
g/G/padditionally keeps the trailing zeros those types trim:{:#.0f}on1gives1.,{:#g}gives1.,{:#.3g}gives1.00. It has no effect one/E. -
Zero-padding (0) – If present and no align is given, pads with zeros after the sign.
-
Width – Minimum field width (integer ≥ 0).
-
Thousands separator (,) – Inserts locale-independent commas (or underscores with
_). -
Precision – Meaning depends on the type:
f/F/e/Enumber of digits after the decimal pointg/Gmaximum number of significant digitsprelative precision from 1 to 15 (default 13) — see Relative precision (p) below
With no precision, every type except
pprints the fewest digits that read back as the same value. This is not printf's default of 6:{:f}on1234.5678gives1234.5678, where C and Python give1234.567800. Logging a float should not silently drop or invent digits. -
Type – Determines presentation type; optional and defaults to
g:
| Type | Meaning | Example (1234.5678) |
|---|---|---|
f / F |
Fixed-point notation (lowercase/uppercase nan/inf) |
1234.5678 / 1234.5678 |
e / E |
Exponential notation (lowercase/uppercase e) |
1.2345678e+03 / 1.2345678E+03 |
g / G |
e for large exponents, f otherwise |
1234.5678 / 1234.5678 |
p |
Fixed-point with relative precision (see AppendSigFixed) |
1234.5678 |
format_spec ::= [[fill] align] [sign] ["#"] ["0"] [width] [grouping] ["." precision] [type]
fill ::= <any character except '{', '}', '<', '>', '^'>
align ::= "<" | ">" | "^" | "="
sign ::= "+" | "-" | " "
grouping ::= "," | "_"
width ::= digit+
precision ::= digit+
digit ::= "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
type ::= "f"|"F"|"e"|"E"|"g"|"G"
| "p"p prints a value in fixed-point notation — never an exponent — carrying a
number of digits that scales with the magnitude, and trims trailing zeros. It is
meant for numbers whose useful precision follows their size, such as prices and
quantities, where f would need a different precision per instrument and g
would flip to exponent notation.
The rule is a single one. Count integer digits as zero below 1, then calculate:
Decimal places =
sig− (number of integer digits).
A positive result is the number of fractional digits. A negative result rounds
inside the integer part: decimal places -3, for example, means rounding to the
nearest thousand. The output remains fixed-point and retains the positional
integer zeros; limiting significant digits does not limit the string length.
For values at or above 1 this makes sig the maximum count of significant
digits. Below 1, sig becomes the count of fractional digits — and since
leading zeros after the point are not significant, the significant digits kept
shrink as the value gets smaller:
| Value | sig = 5 |
sig = 8 |
|---|---|---|
123456789 |
123460000 |
123456790 |
1234.5678 |
1234.6 |
1234.5678 |
12.345678 |
12.346 |
12.345678 |
1.2345678 |
1.2346 |
1.2345678 |
0.12345678 |
0.12346 |
0.12345678 |
0.0012345678 |
0.00123 |
0.00123457 |
1.23e-07 |
0 |
0.00000012 |
Note the last row: a value smaller than sig fractional digits can express
formats as 0, losing it entirely. Pick sig for the smallest magnitude you
need to keep, not for the typical one. The default of 13 keeps values down to
1e-13.
A negative value that rounds away this way prints as 0, not -0. With #
(which keeps trailing zeros) the sign survives, because there -0.000000 still
tells you the value was negative.
For float32 the result never carries more than 9 significant digits, which is
all a float32 distinguishes; asking for more is not an error, it simply stops
adding digits.