Embedding files in Go
Go has a nice property - it compiles to single executable, that could just run. Which does not have depencencies to other packages, does not require runtime or interpreter.
But many of programs could not be built just in Go, and use multiple of different languages that are better fit for their tasks. For example in web you would probably use a lot of SQL, JavaScript, CSS, HTML, JSON, etc... Even desktop apps could use some embedded XML or GLSL, or even CSS for styling.
There are two ways to put such code in your projects: as string literals in Go code, or as separate files. Separate files have advantage of having better modularity, and being more readable (less indentation, and it's easier to highlight file with extension .sql
as SQL).
But text files in project that does not contain Go code have huge disadvantage - they are not included into the executable built by compiler, and need to be loaded during runtime. So you need to add them to Docker image, and distribute docker image instead. Or distribute your program in some other package format, that includes both executable and files.
Four years ago I have built a package & tool to fix that problem and embed text files into go executable as strings: https://github.com/bunyk/require
In that library you could call require.File("filename.txt")
, and it will return content of that file as a string, without actually opening it. There is a hardcode
utility that generates go code for each require.File
call you have in your codebase to work.
But today I noticed two interesting lines in code of Neoray (My GUI of choice for Neovim):
//go:embed shader.glsl
var EmbeddedShaderSources string
Turns out that since Go 1.16, embed functionality is in standard library. Written by Russ Cox and reviewed by Rob Pike (commit). So, obviously it has better design than my package.
Small downside it has, is that you could use it only with global variables. Using embed
directive in function gave me cannot apply to var inside func
compilation error. And it does not work with constants, so you need to be careful to not mutate value yourself.
But, that are not really reasons to not deprecate my library. To quote fortune program used as an example:
One of my most productive days was throwing away 1000 lines of code. — Ken Thompson
package main
import (
_ "embed"
"fmt"
"math/rand"
"strings"
"time"
)
//go:embed fortunes.txt
const contents string
func main() {
fortunes := strings.Split(contents, "\n")
rand.Seed(time.Now().UTC().UnixNano())
fmt.Println(fortunes[rand.Intn(len(fortunes))])
}