aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--content/a-new-go-api-for-protocol-buffers.article76
-rw-r--r--content/appengine-go111.article4
-rw-r--r--content/contributor-workshop.article32
-rw-r--r--content/go-cloud.article92
-rw-r--r--content/go1.13-errors.article228
-rw-r--r--content/h2push.article8
-rw-r--r--content/http-tracing.article4
-rw-r--r--content/migrating-to-go-modules.article220
-rw-r--r--content/module-mirror-launch.article4
-rw-r--r--content/package-names.article6
-rw-r--r--content/pipelines.article8
-rw-r--r--content/v2-go-modules.article104
-rw-r--r--content/wire.article80
13 files changed, 433 insertions, 433 deletions
diff --git a/content/a-new-go-api-for-protocol-buffers.article b/content/a-new-go-api-for-protocol-buffers.article
index 5230db7..f3afe0a 100644
--- a/content/a-new-go-api-for-protocol-buffers.article
+++ b/content/a-new-go-api-for-protocol-buffers.article
@@ -69,35 +69,35 @@ First, we'll write a `.proto` file defining an extension of the
type so we can annotate fields as containing
sensitive information or not.
- syntax = "proto3";
- import "google/protobuf/descriptor.proto";
- package golang.example.policy;
- extend google.protobuf.FieldOptions {
- bool non_sensitive = 50000;
- }
+ syntax = "proto3";
+ import "google/protobuf/descriptor.proto";
+ package golang.example.policy;
+ extend google.protobuf.FieldOptions {
+ bool non_sensitive = 50000;
+ }
We can use this option to mark certain fields as non-sensitive.
- message MyMessage {
- string public_name = 1 [(golang.example.policy.non_sensitive) = true];
- }
+ message MyMessage {
+ string public_name = 1 [(golang.example.policy.non_sensitive) = true];
+ }
Next, we will write a Go function which accepts an arbitrary message
value and removes all the sensitive fields.
- // Redact clears every sensitive field in pb.
- func Redact(pb proto.Message) {
- // ...
- }
+ // Redact clears every sensitive field in pb.
+ func Redact(pb proto.Message) {
+ // ...
+ }
This function accepts a
[[https://pkg.go.dev/google.golang.org/protobuf/proto?tab=doc#Message][`proto.Message`]],
an interface type implemented by all generated message types. This type
is an alias for one defined in the `protoreflect` package:
- type ProtoMessage interface{
- ProtoReflect() Message
- }
+ type ProtoMessage interface{
+ ProtoReflect() Message
+ }
To avoid filling up the namespace of generated
messages, the interface contains only a single method returning a
@@ -112,11 +112,11 @@ The
[[https://pkg.go.dev/google.golang.org/protobuf/reflect/protoreflect?tab=doc#Message.Range][`protoreflect.Message.Range`]]
method calls a function for every populated field in a message.
- m := pb.ProtoReflect()
- m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
- // ...
- return true
- })
+ m := pb.ProtoReflect()
+ m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
+ // ...
+ return true
+ })
The range function is called with a
[[https://pkg.go.dev/google.golang.org/protobuf/reflect/protoreflect?tab=doc#FieldDescriptor][`protoreflect.FieldDescriptor`]]
@@ -129,7 +129,7 @@ The
method returns the field options as a `google.protobuf.FieldOptions`
message.
- opts := fd.Options().(*descriptorpb.FieldOptions)
+ opts := fd.Options().(*descriptorpb.FieldOptions)
(Why the type assertion? Since the generated `descriptorpb` package
depends on `protoreflect`, the `protoreflect` package can't return the
@@ -137,9 +137,9 @@ concrete options type without causing an import cycle.)
We can then check the options to see the value of our extension boolean:
- if proto.GetExtension(opts, policypb.E_NonSensitive).(bool) {
- return true // don't redact non-sensitive fields
- }
+ if proto.GetExtension(opts, policypb.E_NonSensitive).(bool) {
+ return true // don't redact non-sensitive fields
+ }
Note that we are looking at the field _descriptor_ here, not the field
_value_. The information we're interested in lies in the protocol
@@ -155,22 +155,22 @@ not present. Extension decoding errors are reported at `Unmarshal` time.
Once we have identified a field that needs redaction, clearing it is simple:
- m.Clear(fd)
+ m.Clear(fd)
Putting all the above together, our complete redaction function is:
- // Redact clears every sensitive field in pb.
- func Redact(pb proto.Message) {
- m := pb.ProtoReflect()
- m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
- opts := fd.Options().(*descriptorpb.FieldOptions)
- if proto.GetExtension(opts, policypb.E_NonSensitive).(bool) {
- return true
- }
- m.Clear(fd)
- return true
- })
- }
+ // Redact clears every sensitive field in pb.
+ func Redact(pb proto.Message) {
+ m := pb.ProtoReflect()
+ m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
+ opts := fd.Options().(*descriptorpb.FieldOptions)
+ if proto.GetExtension(opts, policypb.E_NonSensitive).(bool) {
+ return true
+ }
+ m.Clear(fd)
+ return true
+ })
+ }
A more complete implementation might recursively descend into
message-valued fields. We hope that this simple example gives a
diff --git a/content/appengine-go111.article b/content/appengine-go111.article
index b2fbb78..3319153 100644
--- a/content/appengine-go111.article
+++ b/content/appengine-go111.article
@@ -55,7 +55,7 @@ supports.
With the application code complete, create an `app.yaml` file to specify
the runtime:
- runtime: go111
+ runtime: go111
Finally, set your machine up with a Google Cloud Platform account:
@@ -65,7 +65,7 @@ Finally, set your machine up with a Google Cloud Platform account:
With all the setup complete, you can deploy using one command:
- gcloud app deploy
+ gcloud app deploy
We think Go developers will find the new Go 1.11 runtime for App Engine an
exciting addition to the available options to run Go applications. There is a
diff --git a/content/contributor-workshop.article b/content/contributor-workshop.article
index 98521ef..3560c6c 100644
--- a/content/contributor-workshop.article
+++ b/content/contributor-workshop.article
@@ -188,12 +188,12 @@ is a great place to start to not miss a step.
The real fun starts when you clone the Go repo. Ironically, you don't hack on
Go under `$GOPATH`, so I put it in my other workspace (which is `~/Develop`).
- cd $DEV # That's my source code folder outside of $GOPATH
- git clone --depth 1 https://go.googlesource.com/go
+ cd $DEV # That's my source code folder outside of $GOPATH
+ git clone --depth 1 https://go.googlesource.com/go
Then install the handy dandy helper tool, `go-contrib-init`:
- go get -u golang.org/x/tools/cmd/go-contrib-init
+ go get -u golang.org/x/tools/cmd/go-contrib-init
Now you can run `go-contrib-init` from the `go/` folder we cloned above and see
whether or not we're ready to contribute. But hold on if you're following along,
@@ -201,7 +201,7 @@ you're not ready just yet.
Next, install `codereview` so you can participate in a Gerrit code review:
- go get -u golang.org/x/review/git-codereview
+ go get -u golang.org/x/review/git-codereview
This package includes `git change` and `git mail` which will replace your
normal workflow of `git commit` and `git push` respectively.
@@ -218,13 +218,13 @@ AND BAM. You're good (to go)! But where to contribute?
In the workshop, they sent us into the `scratch` repository, which is a safe place to
fool around in order to master the workflow:
- cd $(go env GOPATH)/src/golang.org/x
- git clone --depth 1 [[https://go.googlesource.com/scratch][go.googlesource.com/scratch]]
+ cd $(go env GOPATH)/src/golang.org/x
+ git clone --depth 1 [[https://go.googlesource.com/scratch][go.googlesource.com/scratch]]
First stop is to `cd` in and run `go-contrib-init` to make sure you're ready to contribute:
- go-contrib-init
- All good. Happy hacking!
+ go-contrib-init
+ All good. Happy hacking!
From there, I made a folder named after my GitHub account, did a `git add -u`
then took `git change` for a spin. It has a hash that keeps track of your work,
@@ -269,14 +269,14 @@ me through how all examples are actually in-line tests in a `example_test.go`
file. They follow the format of test cases followed by "Output" commented out
and then the answers to the tests. For example:
- func ExampleRegexp_FindString() {
- re := regexp.MustCompile("fo.?")
- fmt.Printf("%q\n", re.FindString("seafood"))
- fmt.Printf("%q\n", re.FindString("meat"))
- // Output:
- // "foo"
- // ""
- }
+ func ExampleRegexp_FindString() {
+ re := regexp.MustCompile("fo.?")
+ fmt.Printf("%q\n", re.FindString("seafood"))
+ fmt.Printf("%q\n", re.FindString("meat"))
+ // Output:
+ // "foo"
+ // ""
+ }
Kind of cool, right?? I followed Francesc's lead and added a function
`ExampleQuoteMeta` and added a few I thought would be helpful. From there it's
diff --git a/content/go-cloud.article b/content/go-cloud.article
index 01ef8aa..6dff340 100644
--- a/content/go-cloud.article
+++ b/content/go-cloud.article
@@ -78,44 +78,44 @@ You can use the generic type [[https://godoc.org/github.com/google/go-cloud/blob
to copy a file from a local disk to a cloud provider.
Let's start by opening an S3 bucket using the included [[https://godoc.org/github.com/google/go-cloud/blob/s3blob][s3blob package]]:
- // setupBucket opens an AWS bucket.
- func setupBucket(ctx context.Context) (*blob.Bucket, error) {
- // Obtain AWS credentials.
- sess, err := session.NewSession(&aws.Config{
- Region: aws.String("us-east-2"),
- })
- if err != nil {
- return nil, err
- }
- // Open a handle to s3://go-cloud-bucket.
- return s3blob.OpenBucket(ctx, sess, "go-cloud-bucket")
- }
+ // setupBucket opens an AWS bucket.
+ func setupBucket(ctx context.Context) (*blob.Bucket, error) {
+ // Obtain AWS credentials.
+ sess, err := session.NewSession(&aws.Config{
+ Region: aws.String("us-east-2"),
+ })
+ if err != nil {
+ return nil, err
+ }
+ // Open a handle to s3://go-cloud-bucket.
+ return s3blob.OpenBucket(ctx, sess, "go-cloud-bucket")
+ }
Once a program has a `*blob.Bucket`, it can create a `*blob.Writer`,
which implements `io.Writer`.
From there, the program can use the `*blob.Writer` to write data to the bucket,
checking that `Close` does not report an error.
- ctx := context.Background()
- b, err := setupBucket(ctx)
- if err != nil {
- log.Fatalf("Failed to open bucket: %v", err)
- }
- data, err := ioutil.ReadFile("gopher.png")
- if err != nil {
- log.Fatalf("Failed to read file: %v", err)
- }
- w, err := b.NewWriter(ctx, "gopher.png", nil)
- if err != nil {
- log.Fatalf("Failed to obtain writer: %v", err)
- }
- _, err = w.Write(data)
- if err != nil {
- log.Fatalf("Failed to write to bucket: %v", err)
- }
- if err := w.Close(); err != nil {
- log.Fatalf("Failed to close: %v", err)
- }
+ ctx := context.Background()
+ b, err := setupBucket(ctx)
+ if err != nil {
+ log.Fatalf("Failed to open bucket: %v", err)
+ }
+ data, err := ioutil.ReadFile("gopher.png")
+ if err != nil {
+ log.Fatalf("Failed to read file: %v", err)
+ }
+ w, err := b.NewWriter(ctx, "gopher.png", nil)
+ if err != nil {
+ log.Fatalf("Failed to obtain writer: %v", err)
+ }
+ _, err = w.Write(data)
+ if err != nil {
+ log.Fatalf("Failed to write to bucket: %v", err)
+ }
+ if err := w.Close(); err != nil {
+ log.Fatalf("Failed to close: %v", err)
+ }
Notice how the logic of using the bucket does not refer to AWS S3.
Go Cloud makes swapping out cloud storage a matter of changing the function
@@ -124,20 +124,20 @@ The application could instead use Google Cloud Storage by constructing a
`*blob.Bucket` using [[https://godoc.org/github.com/google/go-cloud/blob/gcsblob#OpenBucket][`gcsblob.OpenBucket`]]
without changing the code that copies the file:
- // setupBucket opens a GCS bucket.
- func setupBucket(ctx context.Context) (*blob.Bucket, error) {
- // Open GCS bucket.
- creds, err := gcp.DefaultCredentials(ctx)
- if err != nil {
- return nil, err
- }
- c, err := gcp.NewHTTPClient(gcp.DefaultTransport(), gcp.CredentialsTokenSource(creds))
- if err != nil {
- return nil, err
- }
- // Open a handle to gs://go-cloud-bucket.
- return gcsblob.OpenBucket(ctx, "go-cloud-bucket", c)
- }
+ // setupBucket opens a GCS bucket.
+ func setupBucket(ctx context.Context) (*blob.Bucket, error) {
+ // Open GCS bucket.
+ creds, err := gcp.DefaultCredentials(ctx)
+ if err != nil {
+ return nil, err
+ }
+ c, err := gcp.NewHTTPClient(gcp.DefaultTransport(), gcp.CredentialsTokenSource(creds))
+ if err != nil {
+ return nil, err
+ }
+ // Open a handle to gs://go-cloud-bucket.
+ return gcsblob.OpenBucket(ctx, "go-cloud-bucket", c)
+ }
While different steps are needed to access buckets on different cloud providers,
the resulting type used by your application is the same: `*blob.Bucket`.
diff --git a/content/go1.13-errors.article b/content/go1.13-errors.article
index fd3fc4c..8c8c029 100644
--- a/content/go1.13-errors.article
+++ b/content/go1.13-errors.article
@@ -14,12 +14,12 @@ allows Go programmers to add whatever information they desire. All it requires
is a type that implements an `Error` method:
- type QueryError struct {
- Query string
- Err error
- }
+ type QueryError struct {
+ Query string
+ Err error
+ }
- func (e *QueryError) Error() string { return e.Query + ": " + e.Err.Error() }
+ func (e *QueryError) Error() string { return e.Query + ": " + e.Err.Error() }
Error types like this one are ubiquitous, and the information they store varies
widely, from timestamps to filenames to server addresses. Often, that
@@ -42,31 +42,31 @@ Go errors are values. Programs make decisions based on those values in a few
ways. The most common is to compare an error to `nil` to see if an operation
failed.
- if err != nil {
- // something went wrong
- }
+ if err != nil {
+ // something went wrong
+ }
Sometimes we compare an error to a known _sentinel_ value, to see if a specific error has occurred.
- var ErrNotFound = errors.New("not found")
+ var ErrNotFound = errors.New("not found")
- if err == ErrNotFound {
- // something wasn't found
- }
+ if err == ErrNotFound {
+ // something wasn't found
+ }
An error value may be of any type which satisfies the language-defined `error`
interface. A program can use a type assertion or type switch to view an error
value as a more specific type.
- type NotFoundError struct {
- Name string
- }
+ type NotFoundError struct {
+ Name string
+ }
- func (e *NotFoundError) Error() string { return e.Name + ": not found" }
+ func (e *NotFoundError) Error() string { return e.Name + ": not found" }
- if e, ok := err.(*NotFoundError); ok {
- // e.Name wasn't found
- }
+ if e, ok := err.(*NotFoundError); ok {
+ // e.Name wasn't found
+ }
** Adding information
@@ -76,9 +76,9 @@ to it, like a brief description of what was happening when the error occurred. A
simple way to do this is to construct a new error that includes the text of the
previous one:
- if err != nil {
- return fmt.Errorf("decompress %v: %v", name, err)
- }
+ if err != nil {
+ return fmt.Errorf("decompress %v: %v", name, err)
+ }
Creating a new error with `fmt.Errorf` discards everything from the original
error except the text. As we saw above with `QueryError`, we may sometimes want
@@ -86,18 +86,18 @@ to define a new error type that contains the underlying error, preserving it for
inspection by code. Here is `QueryError` again:
- type QueryError struct {
- Query string
- Err error
- }
+ type QueryError struct {
+ Query string
+ Err error
+ }
Programs can look inside a `*QueryError` value to make decisions based on the
underlying error. You'll sometimes see this referred to as "unwrapping" the
error.
- if e, ok := err.(*QueryError); ok && e.Err == ErrPermission {
- // query failed because of a permission problem
- }
+ if e, ok := err.(*QueryError); ok && e.Err == ErrPermission {
+ // query failed because of a permission problem
+ }
The `os.PathError` type in the standard library is another example of one error which contains another.
@@ -116,7 +116,7 @@ that you can _unwrap_ `e1` to get `e2`.
Following this convention, we can give the `QueryError` type above an `Unwrap`
method that returns its contained error:
- func (e *QueryError) Unwrap() error { return e.Err }
+ func (e *QueryError) Unwrap() error { return e.Err }
The result of unwrapping an error may itself have an `Unwrap` method; we call
the sequence of errors produced by repeated unwrapping the _error_chain_.
@@ -127,20 +127,20 @@ The Go 1.13 `errors` package includes two new functions for examining errors: `I
The `errors.Is` function compares an error to a value.
- // Similar to:
- // if err == ErrNotFound { … }
- if errors.Is(err, ErrNotFound) {
- // something wasn't found
- }
+ // Similar to:
+ // if err == ErrNotFound { … }
+ if errors.Is(err, ErrNotFound) {
+ // something wasn't found
+ }
The `As` function tests whether an error is a specific type.
- // Similar to:
- // if e, ok := err.(*QueryError); ok { … }
- var e *QueryError
- if errors.As(err, &e) {
- // err is a *QueryError, and e is set to the error's value
- }
+ // Similar to:
+ // if e, ok := err.(*QueryError); ok { … }
+ var e *QueryError
+ if errors.As(err, &e) {
+ // err is a *QueryError, and e is set to the error's value
+ }
In the simplest case, the `errors.Is` function behaves like a comparison to a
sentinel error, and the `errors.As` function behaves like a type assertion. When
@@ -148,15 +148,15 @@ operating on wrapped errors, however, these functions consider all the errors in
a chain. Let's look again at the example from above of unwrapping a `QueryError`
to examine the underlying error:
- if e, ok := err.(*QueryError); ok && e.Err == ErrPermission {
- // query failed because of a permission problem
- }
+ if e, ok := err.(*QueryError); ok && e.Err == ErrPermission {
+ // query failed because of a permission problem
+ }
Using the `errors.Is` function, we can write this as:
- if errors.Is(err, ErrPermission) {
- // err, or some error that it wraps, is a permission problem
- }
+ if errors.Is(err, ErrPermission) {
+ // err, or some error that it wraps, is a permission problem
+ }
The `errors` package also includes a new `Unwrap` function which returns the
@@ -168,25 +168,25 @@ however, since these functions will examine the entire chain in a single call.
As mentioned earlier, it is common to use the `fmt.Errorf` function to add additional information to an error.
- if err != nil {
- return fmt.Errorf("decompress %v: %v", name, err)
- }
+ if err != nil {
+ return fmt.Errorf("decompress %v: %v", name, err)
+ }
In Go 1.13, the `fmt.Errorf` function supports a new `%w` verb. When this verb
is present, the error returned by `fmt.Errorf` will have an `Unwrap` method
returning the argument of `%w`, which must be an error. In all other ways, `%w`
is identical to `%v`.
- if err != nil {
- // Return an error which unwraps to err.
- return fmt.Errorf("decompress %v: %w", name, err)
- }
+ if err != nil {
+ // Return an error which unwraps to err.
+ return fmt.Errorf("decompress %v: %w", name, err)
+ }
Wrapping an error with `%w` makes it available to `errors.Is` and `errors.As`:
- err := fmt.Errorf("access denied: %w", ErrPermission)
- ...
- if errors.Is(err, ErrPermission) ...
+ err := fmt.Errorf("access denied: %w", ErrPermission)
+ ...
+ if errors.Is(err, ErrPermission) ...
** Whether to Wrap
@@ -215,8 +215,8 @@ then a caller cannot look inside to find the `sql.ErrNoRows`. But if
the function instead returns `fmt.Errorf("accessing`DB:`%w",`err)`, then a
caller could reasonably write
- err := pkg.LookupUser(...)
- if errors.Is(err, sql.ErrNoRows) …
+ err := pkg.LookupUser(...)
+ if errors.Is(err, sql.ErrNoRows) …
At that point, the function must always return `sql.ErrNoRows` if you don't want
to break your clients, even if you switch to a different database package. In
@@ -242,23 +242,23 @@ As an example, consider this error inspired by the
which compares an error against a template, considering only fields which are
non-zero in the template:
- type Error struct {
- Path string
- User string
- }
+ type Error struct {
+ Path string
+ User string
+ }
- func (e *Error) Is(target error) bool {
- t, ok := target.(*Error)
- if !ok {
- return false
- }
- return (e.Path == t.Path || t.Path == "") &&
- (e.User == t.User || t.User == "")
- }
+ func (e *Error) Is(target error) bool {
+ t, ok := target.(*Error)
+ if !ok {
+ return false
+ }
+ return (e.Path == t.Path || t.Path == "") &&
+ (e.User == t.User || t.User == "")
+ }
- if errors.Is(err, &Error{User: "someuser"}) {
- // err's User field is "someuser".
- }
+ if errors.Is(err, &Error{User: "someuser"}) {
+ // err's User field is "someuser".
+ }
The `errors.As` function similarly consults an `As` method when present.
@@ -275,18 +275,18 @@ information is needed.
If we wish a function to return an identifiable error condition, such as "item
not found," we might return an error wrapping a sentinel.
- var ErrNotFound = errors.New("not found")
+ var ErrNotFound = errors.New("not found")
- // FetchItem returns the named item.
- //
- // If no item with the name exists, FetchItem returns an error
- // wrapping ErrNotFound.
- func FetchItem(name string) (*Item, error) {
- if itemNotFound(name) {
- return nil, fmt.Errorf("%q: %w", name, ErrNotFound)
- }
- // ...
- }
+ // FetchItem returns the named item.
+ //
+ // If no item with the name exists, FetchItem returns an error
+ // wrapping ErrNotFound.
+ func FetchItem(name string) (*Item, error) {
+ if itemNotFound(name) {
+ return nil, fmt.Errorf("%q: %w", name, ErrNotFound)
+ }
+ // ...
+ }
There are other existing patterns for providing errors which can be semantically
examined by the caller, such as directly returning a sentinel value, a specific
@@ -298,39 +298,39 @@ an error from another package you should convert the error to a form that does
not expose the underlying error, unless you are willing to commit to returning
that specific error in the future.
- f, err := os.Open(filename)
- if err != nil {
- // The *os.PathError returned by os.Open is an internal detail.
- // To avoid exposing it to the caller, repackage it as a new
- // error with the same text. We use the %v formatting verb, since
- // %w would permit the caller to unwrap the original *os.PathError.
- return fmt.Errorf("%v", err)
- }
+ f, err := os.Open(filename)
+ if err != nil {
+ // The *os.PathError returned by os.Open is an internal detail.
+ // To avoid exposing it to the caller, repackage it as a new
+ // error with the same text. We use the %v formatting verb, since
+ // %w would permit the caller to unwrap the original *os.PathError.
+ return fmt.Errorf("%v", err)
+ }
If a function is defined as returning an error wrapping some sentinel or type,
do not return the underlying error directly.
- var ErrPermission = errors.New("permission denied")
-
- // DoSomething returns an error wrapping ErrPermission if the user
- // does not have permission to do something.
- func DoSomething() error {
- if !userHasPermission() {
- // If we return ErrPermission directly, callers might come
- // to depend on the exact error value, writing code like this:
- //
- // if err := pkg.DoSomething(); err == pkg.ErrPermission { … }
- //
- // This will cause problems if we want to add additional
- // context to the error in the future. To avoid this, we
- // return an error wrapping the sentinel so that users must
- // always unwrap it:
- //
- // if err := pkg.DoSomething(); errors.Is(err, pkg.ErrPermission) { ... }
- return fmt.Errorf("%w", ErrPermission)
- }
- // ...
- }
+ var ErrPermission = errors.New("permission denied")
+
+ // DoSomething returns an error wrapping ErrPermission if the user
+ // does not have permission to do something.
+ func DoSomething() error {
+ if !userHasPermission() {
+ // If we return ErrPermission directly, callers might come
+ // to depend on the exact error value, writing code like this:
+ //
+ // if err := pkg.DoSomething(); err == pkg.ErrPermission { … }
+ //
+ // This will cause problems if we want to add additional
+ // context to the error in the future. To avoid this, we
+ // return an error wrapping the sentinel so that users must
+ // always unwrap it:
+ //
+ // if err := pkg.DoSomething(); errors.Is(err, pkg.ErrPermission) { ... }
+ return fmt.Errorf("%w", ErrPermission)
+ }
+ // ...
+ }
* Conclusion
diff --git a/content/h2push.article b/content/h2push.article
index 9079f8d..519eb92 100644
--- a/content/h2push.article
+++ b/content/h2push.article
@@ -58,7 +58,7 @@ then the `PUSH_PROMISE` should include an Accept-Encoding value:
A fully working example is available at:
- $ go get golang.org/x/blog/content/h2push/server
+ $ go get golang.org/x/blog/content/h2push/server
If you run the server and load [[https://localhost:8080][https://localhost:8080]],
@@ -75,9 +75,9 @@ duplicate responses. For example, suppose you write part of an HTML
response:
- <html>
- <head>
- <link rel="stylesheet" href="a.css">...
+ <html>
+ <head>
+ <link rel="stylesheet" href="a.css">...
Then you call Push("a.css", nil). The browser may parse this fragment
diff --git a/content/http-tracing.article b/content/http-tracing.article
index ae14d57..710ae60 100644
--- a/content/http-tracing.article
+++ b/content/http-tracing.article
@@ -57,8 +57,8 @@ The program below identifies the current request by using an
The program will follow the redirect of google.com to www.google.com and will output:
- Connection reused for https://google.com? false
- Connection reused for https://www.google.com/? false
+ Connection reused for https://google.com? false
+ Connection reused for https://www.google.com/? false
The Transport in the `net/http` package supports tracing of both HTTP/1
and HTTP/2 requests.
diff --git a/content/migrating-to-go-modules.article b/content/migrating-to-go-modules.article
index 3e5fbc5..e3c8759 100644
--- a/content/migrating-to-go-modules.article
+++ b/content/migrating-to-go-modules.article
@@ -45,37 +45,37 @@ we'll address the latter two in this post.
To convert a project that already uses a dependency management tool, run the following commands:
- $ git clone https://github.com/my/project
- [...]
- $ cd project
- $ cat Godeps/Godeps.json
- {
- "ImportPath": "github.com/my/project",
- "GoVersion": "go1.12",
- "GodepVersion": "v80",
- "Deps": [
- {
- "ImportPath": "rsc.io/binaryregexp",
- "Comment": "v0.2.0-1-g545cabd",
- "Rev": "545cabda89ca36b48b8e681a30d9d769a30b3074"
- },
- {
- "ImportPath": "rsc.io/binaryregexp/syntax",
- "Comment": "v0.2.0-1-g545cabd",
- "Rev": "545cabda89ca36b48b8e681a30d9d769a30b3074"
- }
- ]
- }
- $ go mod init github.com/my/project
- go: creating new go.mod: module github.com/my/project
- go: copying requirements from Godeps/Godeps.json
- $ cat go.mod
- module github.com/my/project
-
- go 1.12
-
- require rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca
- $
+ $ git clone https://github.com/my/project
+ [...]
+ $ cd project
+ $ cat Godeps/Godeps.json
+ {
+ "ImportPath": "github.com/my/project",
+ "GoVersion": "go1.12",
+ "GodepVersion": "v80",
+ "Deps": [
+ {
+ "ImportPath": "rsc.io/binaryregexp",
+ "Comment": "v0.2.0-1-g545cabd",
+ "Rev": "545cabda89ca36b48b8e681a30d9d769a30b3074"
+ },
+ {
+ "ImportPath": "rsc.io/binaryregexp/syntax",
+ "Comment": "v0.2.0-1-g545cabd",
+ "Rev": "545cabda89ca36b48b8e681a30d9d769a30b3074"
+ }
+ ]
+ }
+ $ go mod init github.com/my/project
+ go: creating new go.mod: module github.com/my/project
+ go: copying requirements from Godeps/Godeps.json
+ $ cat go.mod
+ module github.com/my/project
+
+ go 1.12
+
+ require rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca
+ $
`go`mod`init` creates a new go.mod file and automatically imports dependencies from `Godeps.json`,
`Gopkg.lock`, or a number of [[https://go.googlesource.com/go/+/362625209b6cd2bc059b6b0a67712ddebab312d9/src/cmd/go/internal/modconv/modconv.go#9][other supported formats]].
@@ -87,13 +87,13 @@ Later steps may modify your `go.mod` file,
so if you prefer to take an iterative approach,
this is the closest your `go.mod` file will be to your pre-modules dependency specification.
- $ go mod tidy
- go: downloading rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca
- go: extracting rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca
- $ cat go.sum
- rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca h1:FKXXXJ6G2bFoVe7hX3kEX6Izxw5ZKRH57DFBJmHCbkU=
- rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
- $
+ $ go mod tidy
+ go: downloading rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca
+ go: extracting rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca
+ $ cat go.sum
+ rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca h1:FKXXXJ6G2bFoVe7hX3kEX6Izxw5ZKRH57DFBJmHCbkU=
+ rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+ $
`go`mod`tidy` finds all the packages transitively imported by packages in your module.
It adds new module requirements for packages not provided by any known module,
@@ -106,10 +106,10 @@ file to version control.
Let's finish by making sure the code builds and tests pass:
- $ go build ./...
- $ go test ./...
- [...]
- $
+ $ go build ./...
+ $ go test ./...
+ [...]
+ $
Note that other dependency managers may specify dependencies at the level
of individual packages or entire repositories (not modules),
@@ -120,11 +120,11 @@ and there's some risk of upgrading past breaking changes.
Therefore, it's important to follow the above commands with an audit of
the resulting dependencies. To do so, run
- $ go list -m all
- go: finding rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca
- github.com/my/project
- rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca
- $
+ $ go list -m all
+ go: finding rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca
+ github.com/my/project
+ rsc.io/binaryregexp v0.2.1-0.20190524193500-545cabda89ca
+ $
and compare the resulting versions with your old dependency management file
to ensure that the selected versions are appropriate.
@@ -134,27 +134,27 @@ and upgrade or downgrade to the correct version using `go`get`.
(If the version you request is older than the version that was previously selected,
`go`get` will downgrade other dependencies as needed to maintain compatibility.) For example,
- $ go mod why -m rsc.io/binaryregexp
- [...]
- $ go mod graph | grep rsc.io/binaryregexp
- [...]
- $ go get rsc.io/binaryregexp@v0.2.0
- $
+ $ go mod why -m rsc.io/binaryregexp
+ [...]
+ $ go mod graph | grep rsc.io/binaryregexp
+ [...]
+ $ go get rsc.io/binaryregexp@v0.2.0
+ $
* Without a dependency manager
For a Go project without a dependency management system, start by creating a `go.mod` file:
- $ git clone https://go.googlesource.com/blog
- [...]
- $ cd blog
- $ go mod init golang.org/x/blog
- go: creating new go.mod: module golang.org/x/blog
- $ cat go.mod
- module golang.org/x/blog
+ $ git clone https://go.googlesource.com/blog
+ [...]
+ $ cd blog
+ $ go mod init golang.org/x/blog
+ go: creating new go.mod: module golang.org/x/blog
+ $ cat go.mod
+ module golang.org/x/blog
- go 1.12
- $
+ go 1.12
+ $
Without a configuration file from a previous dependency manager,
`go`mod`init` will create a `go.mod` file with only the `module` and `go` directives.
@@ -169,58 +169,58 @@ used to compile the code within the module.
Next, run `go`mod`tidy` to add the module's dependencies:
- $ go mod tidy
- go: finding golang.org/x/website latest
- go: finding gopkg.in/tomb.v2 latest
- go: finding golang.org/x/net latest
- go: finding golang.org/x/tools latest
- go: downloading github.com/gorilla/context v1.1.1
- go: downloading golang.org/x/tools v0.0.0-20190813214729-9dba7caff850
- go: downloading golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
- go: extracting github.com/gorilla/context v1.1.1
- go: extracting golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
- go: downloading gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
- go: extracting gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
- go: extracting golang.org/x/tools v0.0.0-20190813214729-9dba7caff850
- go: downloading golang.org/x/website v0.0.0-20190809153340-86a7442ada7c
- go: extracting golang.org/x/website v0.0.0-20190809153340-86a7442ada7c
- $ cat go.mod
- module golang.org/x/blog
-
- go 1.12
-
- require (
- github.com/gorilla/context v1.1.1
- golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
- golang.org/x/text v0.3.2
- golang.org/x/tools v0.0.0-20190813214729-9dba7caff850
- golang.org/x/website v0.0.0-20190809153340-86a7442ada7c
- gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
- )
- $ cat go.sum
- cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
- cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
- git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
- git.apache.org/thrift.git v0.0.0-20181218151757-9b75e4fe745a/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
- github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
- [...]
- $
+ $ go mod tidy
+ go: finding golang.org/x/website latest
+ go: finding gopkg.in/tomb.v2 latest
+ go: finding golang.org/x/net latest
+ go: finding golang.org/x/tools latest
+ go: downloading github.com/gorilla/context v1.1.1
+ go: downloading golang.org/x/tools v0.0.0-20190813214729-9dba7caff850
+ go: downloading golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
+ go: extracting github.com/gorilla/context v1.1.1
+ go: extracting golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
+ go: downloading gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
+ go: extracting gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
+ go: extracting golang.org/x/tools v0.0.0-20190813214729-9dba7caff850
+ go: downloading golang.org/x/website v0.0.0-20190809153340-86a7442ada7c
+ go: extracting golang.org/x/website v0.0.0-20190809153340-86a7442ada7c
+ $ cat go.mod
+ module golang.org/x/blog
+
+ go 1.12
+
+ require (
+ github.com/gorilla/context v1.1.1
+ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
+ golang.org/x/text v0.3.2
+ golang.org/x/tools v0.0.0-20190813214729-9dba7caff850
+ golang.org/x/website v0.0.0-20190809153340-86a7442ada7c
+ gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
+ )
+ $ cat go.sum
+ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
+ git.apache.org/thrift.git v0.0.0-20181218151757-9b75e4fe745a/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
+ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+ [...]
+ $
`go`mod`tidy` added module requirements for all the packages transitively
imported by packages in your module and built a `go.sum` with checksums
for each library at a specific version.
Let's finish by making sure the code still builds and tests still pass:
- $ go build ./...
- $ go test ./...
- ok golang.org/x/blog 0.335s
- ? golang.org/x/blog/content/appengine [no test files]
- ok golang.org/x/blog/content/cover 0.040s
- ? golang.org/x/blog/content/h2push/server [no test files]
- ? golang.org/x/blog/content/survey2016 [no test files]
- ? golang.org/x/blog/content/survey2017 [no test files]
- ? golang.org/x/blog/support/racy [no test files]
- $
+ $ go build ./...
+ $ go test ./...
+ ok golang.org/x/blog 0.335s
+ ? golang.org/x/blog/content/appengine [no test files]
+ ok golang.org/x/blog/content/cover 0.040s
+ ? golang.org/x/blog/content/h2push/server [no test files]
+ ? golang.org/x/blog/content/survey2016 [no test files]
+ ? golang.org/x/blog/content/survey2017 [no test files]
+ ? golang.org/x/blog/support/racy [no test files]
+ $
Note that when `go`mod`tidy` adds a requirement,
it adds the latest version of the module.
@@ -260,8 +260,8 @@ but without an official release, downstream users will depend on specific
commits using [[https://golang.org/cmd/go/#hdr-Pseudo_versions][pseudo-versions]],
which may be more difficult to support.
- $ git tag v1.2.0
- $ git push origin v1.2.0
+ $ git tag v1.2.0
+ $ git push origin v1.2.0
Your new `go.mod` file defines a canonical import path for your module and adds
new minimum version requirements. If your users are already using the correct
diff --git a/content/module-mirror-launch.article b/content/module-mirror-launch.article
index 2bef6e4..d86a716 100644
--- a/content/module-mirror-launch.article
+++ b/content/module-mirror-launch.article
@@ -116,8 +116,8 @@ If you are using Go 1.12 or earlier, you can manually check a `go.sum` file
against the checksum database with
[[https://godoc.org/golang.org/x/mod/gosumcheck][gosumcheck]]:
- $ go get golang.org/x/mod/gosumcheck
- $ gosumcheck /path/to/go.sum
+ $ go get golang.org/x/mod/gosumcheck
+ $ gosumcheck /path/to/go.sum
In addition to verification done by the `go` command, third-party
auditors can hold the checksum database accountable by iterating over the log
diff --git a/content/package-names.article b/content/package-names.article
index 967f02c..2cd74d1 100644
--- a/content/package-names.article
+++ b/content/package-names.article
@@ -86,13 +86,13 @@ When a function in package pkg returns a value of type `pkg.Pkg` (or
start := time.Now() // start is a time.Time
t, err := time.Parse(time.Kitchen, "6:06PM") // t is a time.Time
- ctx = context.WithTimeout(ctx, 10*time.Millisecond) // ctx is a context.Context
- ip, ok := userip.FromContext(ctx) // ip is a net.IP
+ ctx = context.WithTimeout(ctx, 10*time.Millisecond) // ctx is a context.Context
+ ip, ok := userip.FromContext(ctx) // ip is a net.IP
A function named `New` in package `pkg` returns a value of type `pkg.Pkg`.
This is a standard entry point for client code using that type:
- q := list.New() // q is a *list.List
+ q := list.New() // q is a *list.List
When a function returns a value of type `pkg.T`, where `T` is not `Pkg`, the
function name may include `T` to make client code easier to understand.
diff --git a/content/pipelines.article b/content/pipelines.article
index de145a1..194b26c 100644
--- a/content/pipelines.article
+++ b/content/pipelines.article
@@ -122,10 +122,10 @@ to change the outbound channels to have a buffer. A buffer can hold a fixed
number of values; send operations complete immediately if there's room in the
buffer:
- c := make(chan int, 2) // buffer size 2
- c <- 1 // succeeds immediately
- c <- 2 // succeeds immediately
- c <- 3 // blocks until another goroutine does <-c and receives 1
+ c := make(chan int, 2) // buffer size 2
+ c <- 1 // succeeds immediately
+ c <- 2 // succeeds immediately
+ c <- 3 // blocks until another goroutine does <-c and receives 1
When the number of values to be sent is known at channel creation time, a buffer
can simplify the code. For example, we can rewrite `gen` to copy the list of
diff --git a/content/v2-go-modules.article b/content/v2-go-modules.article
index db2c0cd..54b7e5a 100644
--- a/content/v2-go-modules.article
+++ b/content/v2-go-modules.article
@@ -34,8 +34,8 @@ major version, and how to maintain multiple major versions of a module.
Modules formalized an important principle in Go, the
[[https://research.swtch.com/vgo-import][*import*compatibility*rule*]]:
- If an old package and a new package have the same import path,
- the new package must be backwards compatible with the old package.
+ If an old package and a new package have the same import path,
+ the new package must be backwards compatible with the old package.
By definition, a new major version of a package is not backwards compatible with
the previous version. This means a new major version of a module must have a
@@ -64,9 +64,9 @@ domains need a slash suffix like `/v2`.
The recommended strategy is to develop `v2+` modules in a directory named after
the major version suffix.
- github.com/googleapis/gax-go @ master branch
- /go.mod → module github.com/googleapis/gax-go
- /v2/go.mod → module github.com/googleapis/gax-go/v2
+ github.com/googleapis/gax-go @ master branch
+ /go.mod → module github.com/googleapis/gax-go
+ /v2/go.mod → module github.com/googleapis/gax-go/v2
This approach is compatible with tools that aren't aware of modules: file paths
within the repository match the paths expected by `go`get` in `GOPATH` mode.
@@ -86,51 +86,51 @@ follow this strategy as long as they have users developing in `GOPATH` mode.
This post uses `github.com/googleapis/gax-go` as an example:
- $ pwd
- /tmp/gax-go
- $ ls
- CODE_OF_CONDUCT.md call_option.go internal
- CONTRIBUTING.md gax.go invoke.go
- LICENSE go.mod tools.go
- README.md go.sum RELEASING.md
- header.go
- $ cat go.mod
- module github.com/googleapis/gax-go
-
- go 1.9
-
- require (
- github.com/golang/protobuf v1.3.1
- golang.org/x/exp v0.0.0-20190221220918-438050ddec5e
- golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3
- golang.org/x/tools v0.0.0-20190114222345-bf090417da8b
- google.golang.org/grpc v1.19.0
- honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099
- )
- $
+ $ pwd
+ /tmp/gax-go
+ $ ls
+ CODE_OF_CONDUCT.md call_option.go internal
+ CONTRIBUTING.md gax.go invoke.go
+ LICENSE go.mod tools.go
+ README.md go.sum RELEASING.md
+ header.go
+ $ cat go.mod
+ module github.com/googleapis/gax-go
+
+ go 1.9
+
+ require (
+ github.com/golang/protobuf v1.3.1
+ golang.org/x/exp v0.0.0-20190221220918-438050ddec5e
+ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3
+ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b
+ google.golang.org/grpc v1.19.0
+ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099
+ )
+ $
To start development on `v2` of `github.com/googleapis/gax-go`, we'll create a
new `v2/` directory and copy our package into it.
- $ mkdir v2
- $ cp *.go v2/
- building file list ... done
- call_option.go
- gax.go
- header.go
- invoke.go
- tools.go
-
- sent 10588 bytes received 130 bytes 21436.00 bytes/sec
- total size is 10208 speedup is 0.95
- $
+ $ mkdir v2
+ $ cp *.go v2/
+ building file list ... done
+ call_option.go
+ gax.go
+ header.go
+ invoke.go
+ tools.go
+
+ sent 10588 bytes received 130 bytes 21436.00 bytes/sec
+ total size is 10208 speedup is 0.95
+ $
Now, let's create a v2 `go.mod` file by copying the current `go.mod` file and
adding a `v2/` suffix to the module path:
- $ cp go.mod v2/go.mod
- $ go mod edit -module github.com/googleapis/gax-go/v2 v2/go.mod
- $
+ $ cp go.mod v2/go.mod
+ $ go mod edit -module github.com/googleapis/gax-go/v2 v2/go.mod
+ $
Note that the `v2` version is treated as a separate module from the `v0`/`v1`
versions: both may coexist in the same build. So, if your `v2+` module has
@@ -139,10 +139,10 @@ otherwise, your `v2+` module will depend on your `v0`/`v1` module. For example,
to update all `github.com/my/project` references to `github.com/my/project/v2`,
you can use `find` and `sed`:
- $ find . -type f \
- -name '*.go' \
- -exec sed -i -e 's,github.com/my/project,github.com/my/project/v2,g' {} \;
- $
+ $ find . -type f \
+ -name '*.go' \
+ -exec sed -i -e 's,github.com/my/project,github.com/my/project/v2,g' {} \;
+ $
Now we have a `v2` module, but we want to experiment and make changes before
publishing a release. Until we release `v2.0.0` (or any version without a
@@ -150,16 +150,16 @@ pre-release suffix), we can develop and make breaking changes as we decide on
the new API. If we want users to be able to experiment with the new API before
we officially make it stable, we can publish a `v2` pre-release version:
- $ git tag v2.0.0-alpha.1
- $ git push origin v2.0.0-alpha.1
- $
+ $ git tag v2.0.0-alpha.1
+ $ git push origin v2.0.0-alpha.1
+ $
Once we are happy with our `v2` API and are sure we don't need any other breaking
changes, we can tag `v2.0.0`:
- $ git tag v2.0.0
- $ git push origin v2.0.0
- $
+ $ git tag v2.0.0
+ $ git push origin v2.0.0
+ $
At that point, there are now two major versions to maintain. Backwards
compatible changes and bug fixes will lead to new minor and patch releases
diff --git a/content/wire.article b/content/wire.article
index 40b8f33..2d9ca5b 100644
--- a/content/wire.article
+++ b/content/wire.article
@@ -18,8 +18,8 @@ is a standard technique for producing flexible and loosely coupled code,
by explicitly providing components with all of the dependencies they need to work.
In Go, this often takes the form of passing dependencies to constructors:
- // NewUserStore returns a UserStore that uses cfg and db as dependencies.
- func NewUserStore(cfg *Config, db *mysql.DB) (*UserStore, error) {...}
+ // NewUserStore returns a UserStore that uses cfg and db as dependencies.
+ func NewUserStore(cfg *Config, db *mysql.DB) (*UserStore, error) {...}
This technique works great at small scale,
but larger applications can have a complex graph of dependencies,
@@ -101,69 +101,69 @@ _Providers_ are ordinary Go functions that "provide" values given their dependen
which are described simply as parameters to the function.
Here's some sample code that defines three providers:
- // NewUserStore is the same function we saw above; it is a provider for UserStore,
- // with dependencies on *Config and *mysql.DB.
- func NewUserStore(cfg *Config, db *mysql.DB) (*UserStore, error) {...}
+ // NewUserStore is the same function we saw above; it is a provider for UserStore,
+ // with dependencies on *Config and *mysql.DB.
+ func NewUserStore(cfg *Config, db *mysql.DB) (*UserStore, error) {...}
- // NewDefaultConfig is a provider for *Config, with no dependencies.
- func NewDefaultConfig() *Config {...}
+ // NewDefaultConfig is a provider for *Config, with no dependencies.
+ func NewDefaultConfig() *Config {...}
- // NewDB is a provider for *mysql.DB based on some connection info.
- func NewDB(info *ConnectionInfo) (*mysql.DB, error) {...}
+ // NewDB is a provider for *mysql.DB based on some connection info.
+ func NewDB(info *ConnectionInfo) (*mysql.DB, error) {...}
Providers that are commonly used together can be grouped into `ProviderSets`.
For example, it's common to use a default `*Config` when creating a `*UserStore`,
so we can group `NewUserStore` and `NewDefaultConfig` in a `ProviderSet`:
- var UserStoreSet = wire.ProviderSet(NewUserStore, NewDefaultConfig)
+ var UserStoreSet = wire.ProviderSet(NewUserStore, NewDefaultConfig)
_Injectors_ are generated functions that call providers in dependency order.
You write the injector's signature, including any needed inputs as arguments,
and insert a call to `wire.Build` with the list of providers or provider
sets that are needed to construct the end result:
- func initUserStore() (*UserStore, error) {
- // We're going to get an error, because NewDB requires a *ConnectionInfo
- // and we didn't provide one.
- wire.Build(UserStoreSet, NewDB)
- return nil, nil // These return values are ignored.
- }
+ func initUserStore() (*UserStore, error) {
+ // We're going to get an error, because NewDB requires a *ConnectionInfo
+ // and we didn't provide one.
+ wire.Build(UserStoreSet, NewDB)
+ return nil, nil // These return values are ignored.
+ }
Now we run go generate to execute wire:
- $ go generate
- wire.go:2:10: inject initUserStore: no provider found for ConnectionInfo (required by provider of *mysql.DB)
- wire: generate failed
+ $ go generate
+ wire.go:2:10: inject initUserStore: no provider found for ConnectionInfo (required by provider of *mysql.DB)
+ wire: generate failed
Oops! We didn't include a `ConnectionInfo` or tell Wire how to build one.
Wire helpfully tells us the line number and types involved.
We can either add a provider for it to `wire.Build`,
or add it as an argument:
- func initUserStore(info ConnectionInfo) (*UserStore, error) {
- wire.Build(UserStoreSet, NewDB)
- return nil, nil // These return values are ignored.
- }
+ func initUserStore(info ConnectionInfo) (*UserStore, error) {
+ wire.Build(UserStoreSet, NewDB)
+ return nil, nil // These return values are ignored.
+ }
Now `go generate` will create a new file with the generated code:
- // File: wire_gen.go
- // Code generated by Wire. DO NOT EDIT.
- //go:generate wire
- //+build !wireinject
-
- func initUserStore(info ConnectionInfo) (*UserStore, error) {
- defaultConfig := NewDefaultConfig()
- db, err := NewDB(info)
- if err != nil {
- return nil, err
- }
- userStore, err := NewUserStore(defaultConfig, db)
- if err != nil {
- return nil, err
- }
- return userStore, nil
- }
+ // File: wire_gen.go
+ // Code generated by Wire. DO NOT EDIT.
+ //go:generate wire
+ //+build !wireinject
+
+ func initUserStore(info ConnectionInfo) (*UserStore, error) {
+ defaultConfig := NewDefaultConfig()
+ db, err := NewDB(info)
+ if err != nil {
+ return nil, err
+ }
+ userStore, err := NewUserStore(defaultConfig, db)
+ if err != nil {
+ return nil, err
+ }
+ return userStore, nil
+ }
Any non-injector declarations are copied into the generated file.
There is no dependency on Wire at runtime: