It is an error to import a package into a file but not refer to the
name it defines within that file.
However, on occasion we must import a package merely for the side
effects of doing so: evaluation of the initializer expressions of its
package-level variables and execution of its init
functions
(§2.6.2).
To suppress the “unused import” error we would otherwise encounter, we
must use a renaming import in which the alternative name is _
, the
blank identifier.
As usual, the blank identifier can never be referenced.
import _ "image/png" // register PNG decoder
This is known as a blank import. It is most often used to implement a compile-time mechanism whereby the main program can enable optional features by blank-importing additional packages. First we’ll see how to use it, then we’ll see how it works.
The standard library’s image
package exports a Decode
function that reads bytes from an io.Reader
, figures out which image
format was used to encode the data, invokes the appropriate decoder,
then returns the resulting image.Image
.
Using image.Decode
, it’s easy to build a simple image converter
that reads an image in one format and writes it out in another:
// The jpeg command reads a PNG image from the standard input // and writes it as a JPEG image to the standard output. package main import ( "fmt" "image" "image/jpeg" _ "image/png" // register PNG decoder "io" "os" ) func main() { if err := toJPEG(os.Stdin, os.Stdout); err != nil { fmt.Fprintf(os.Stderr, "jpeg: %v ", err) os.Exit(1) } } func toJPEG(in io.Reader, out io.Writer) error { img, kind, err := image.Decode(in) if err != nil { return err } fmt.Fprintln(os.Stderr, "Input format =", kind) return jpeg.Encode(out, img, &jpeg.Options{Quality: 95}) }
If we feed the output of
gopl.io/ch3/mandelbrot
(§3.3)
to the converter program, it detects
the PNG input format and writes a JPEG version of Figure 3.3.
$ go build gopl.io/ch3/mandelbrot $ go build gopl.io/ch10/jpeg $ ./mandelbrot | ./jpeg >mandelbrot.jpg Input format = png
Notice the blank import of image/png
.
Without that line, the program compiles and links as usual
but can no longer recognize or decode input in PNG format:
$ go build gopl.io/ch10/jpeg $ ./mandelbrot | ./jpeg >mandelbrot.jpg jpeg: image: unknown format
Here’s how it works.
The standard library provides decoders for GIF, PNG, and JPEG, and
users may provide others, but to keep executables small, decoders are
not included in an application unless explicitly requested.
The image.Decode
function consults a table of supported formats.
Each entry in the table specifies four things: the name of the format;
a string that is a prefix of all images encoded this way, used to
detect the encoding; a function Decode
that decodes an encoded
image; and another function DecodeConfig
that decodes only the
image metadata, such as its size and color space.
An entry is added to the table by calling image.RegisterFormat
,
typically from within the package initializer of the supporting
package for each format, like this one in image/png
:
package png // image/png func Decode(r io.Reader) (image.Image, error) func DecodeConfig(r io.Reader) (image.Config, error) func init() { const pngHeader = "x89PNG x1a " image.RegisterFormat("png", pngHeader, Decode, DecodeConfig) }
The effect is that an application need only blank-import the
package for the format it needs to make the image.Decode
function able to decode it.
The database/sql
package uses a similar mechanism to let
users install just the database drivers they need.
For example:
import ( "database/sql" _ "github.com/lib/pq" // enable support for Postgres _ "github.com/go-sql-driver/mysql" // enable support for MySQL ) db, err = sql.Open("postgres", dbname) // OK db, err = sql.Open("mysql", dbname) // OK db, err = sql.Open("sqlite3", dbname) // returns error: unknown driver "sqlite3"
Exercise 10.1:
Extend the jpeg
program so that it converts any supported input
format to any output format, using image.Decode
to detect the
input format and a flag to select the output format.
Exercise 10.2:
Define a generic archive file-reading function capable of reading ZIP
files (archive/zip
) and POSIX tar files (archive/tar
).
Use a registration mechanism similar to the one described above so
that support for each file format can be plugged in using blank
imports.
3.81.57.77