diff options
-rw-r--r-- | content/a-new-go-api-for-protocol-buffers.article | 76 | ||||
-rw-r--r-- | content/appengine-go111.article | 4 | ||||
-rw-r--r-- | content/contributor-workshop.article | 32 | ||||
-rw-r--r-- | content/go-cloud.article | 92 | ||||
-rw-r--r-- | content/go1.13-errors.article | 228 | ||||
-rw-r--r-- | content/h2push.article | 8 | ||||
-rw-r--r-- | content/http-tracing.article | 4 | ||||
-rw-r--r-- | content/migrating-to-go-modules.article | 220 | ||||
-rw-r--r-- | content/module-mirror-launch.article | 4 | ||||
-rw-r--r-- | content/package-names.article | 6 | ||||
-rw-r--r-- | content/pipelines.article | 8 | ||||
-rw-r--r-- | content/v2-go-modules.article | 104 | ||||
-rw-r--r-- | content/wire.article | 80 |
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: |