While working on boot.dev’s Go Playground, I came across a very strange error. The standard library’s fmt.Printf() function prints nothing to the console when called. Nothing.
For those of you who are familiar with the function, when compiled to a “normal” executable fmt.Printf
prints a formatted string to standard output. As per the official documentation, this program:
package main
import (
"fmt"
)
func main() {
const name, age = "Kim", 22
fmt.Printf("%s is %d years old.", name, age)
}
Will print:
Kim is 22 years old.
The interesting thing is that when the same exact program is compiled using Web Assembly, we get a different result. If you want to try it, copy the above program and run it here.
Spoiler alert: It doesn’t print anything.
However, if you change the program slightly:
package main
import (
"fmt"
)
func main() {
const name, age = "Kim", 22
// add a newline character
fmt.Printf("%s is %d years old.\n", name, age)
}
Then it may print the expected:
Kim is 22 years old.
thought if you run the two programs back to back, you may actually find that this is printed instead:
Kim is 22 years old.Kim is 22 years old.
Why? 🔗
When compiled to Web Assembly, the fmt.Printf
function is writing to a buffer, and that buffer is not cleared until a newline character is printed to standard out. In other words, you can call fmt.Printf
as many times as you want, but nothing is printed until a \n
character comes through standard output.
Take a look at the writeSync()
code in Go’s wasm_exec.js
, which is a required dependency to execute Go Web Assembly in the browser:
writeSync(fd, buf) {
outputBuf += decoder.decode(buf);
const nl = outputBuf.lastIndexOf("\n");
if (nl != -1) {
console.log(outputBuf.substr(0, nl));
outputBuf = outputBuf.substr(nl + 1);
}
return buf.length;
}
As you can see, console.log()
is only called on the buffer if a newline is found within the output string, otherwise, the output is just appended to the stateful outputBuf
.
My current working theory is that it was implemented this way because there is no way to print to the console in a browser without appending a new line. JavaScript’s console.log() always appends a new line.