diff options
Diffstat (limited to 'doc/effective_go.html')
-rw-r--r-- | doc/effective_go.html | 136 |
1 files changed, 117 insertions, 19 deletions
diff --git a/doc/effective_go.html b/doc/effective_go.html index 1b3168683..f9199511a 100644 --- a/doc/effective_go.html +++ b/doc/effective_go.html @@ -44,8 +44,8 @@ use the language. Moreover, many of the packages contain working, self-contained executable examples you can run directly from the <a href="http://golang.org">golang.org</a> web site, such as -<a href="http://golang.org/pkg/strings/#example_Map">this one</a> (click -on the word "Example" to open it up). +<a href="http://golang.org/pkg/strings/#example_Map">this one</a> (if +necessary, click on the word "Example" to open it up). If you have a question about how to approach a problem or how something might be implemented, the documentation, code and examples in the library can provide answers, ideas and @@ -506,6 +506,8 @@ slightly generalized <code>switch</code> is more flexible; <code>if</code> and <code>switch</code> accept an optional initialization statement like that of <code>for</code>; +<code>break</code> and <code>continue</code> statements +take an optional label to identify what to break or continue; and there are new control structures including a type switch and a multiway communications multiplexer, <code>select</code>. The syntax is also slightly different: @@ -781,7 +783,47 @@ func shouldEscape(c byte) bool { </pre> <p> -Here's a comparison routine for byte slices that uses two +Although they are not nearly as common in Go as some other C-like +languages, <code>break</code> statements can be used to terminate +a <code>switch</code> early. +Sometimes, though, it's necessary to break out of a surrounding loop, +not the switch, and in Go that can be accomplished by putting a label +on the loop and "breaking" to that label. +This example shows both uses. +</p> + +<pre> +Loop: + for n := 0; n < len(src); n += size { + switch { + case src[n] < sizeOne: + if validateOnly { + break + } + size = 1 + update(src[n]) + + case src[n] < sizeTwo: + if n+1 >= len(src) { + err = errShortInput + break Loop + } + if validateOnly { + break + } + size = 2 + update(src[n] + src[n+1]<<shift) + } + } +</pre> + +<p> +Of course, the <code>continue</code> statement also accepts an optional label +but it applies only to loops. +</p> + +<p> +To close this section, here's a comparison routine for byte slices that uses two <code>switch</code> statements: </p> <pre> @@ -798,10 +840,10 @@ func Compare(a, b []byte) int { } } switch { - case len(a) < len(b): - return -1 case len(a) > len(b): return 1 + case len(a) < len(b): + return -1 } return 0 } @@ -882,7 +924,7 @@ func nextInt(b []byte, i int) (int, int) { } x := 0 for ; i < len(b) && isDigit(b[i]); i++ { - x = x*10 + int(b[i])-'0' + x = x*10 + int(b[i]) - '0' } return x, i } @@ -1496,7 +1538,7 @@ with colon-separated key-value pairs, so it's easy to build them during initialization. </p> <pre> -var timeZone = map[string] int { +var timeZone = map[string]int{ "UTC": 0*60*60, "EST": -5*60*60, "CST": -6*60*60, @@ -1523,7 +1565,7 @@ Set the map entry to <code>true</code> to put the value in the set, and then test it by simple indexing. </p> <pre> -attended := map[string] bool { +attended := map[string]bool{ "Ann": true, "Joe": true, ... @@ -1915,7 +1957,7 @@ initializer can be a general expression computed at run time. var ( home = os.Getenv("HOME") user = os.Getenv("USER") - goRoot = os.Getenv("GOROOT") + gopath = os.Getenv("GOPATH") ) </pre> @@ -1944,11 +1986,11 @@ func init() { if home == "" { home = "/home/" + user } - if goRoot == "" { - goRoot = home + "/go" + if gopath == "" { + gopath = home + "/go" } - // goRoot may be overridden by --goroot flag on command line. - flag.StringVar(&goRoot, "goroot", goRoot, "Go root directory") + // gopath may be overridden by --gopath flag on command line. + flag.StringVar(&gopath, "gopath", gopath, "override default GOPATH") } </pre> @@ -2536,7 +2578,7 @@ package, which defines a <code><a href="/pkg/encoding/json/#Marshaler">Marshaler interface. When the JSON encoder receives a value that implements that interface, the encoder invokes the value's marshaling method to convert it to JSON instead of doing the standard conversion. -The encoder checks this property at run time with a <a href="interface_conversions">type assertion</a> like: +The encoder checks this property at run time with a <a href="#interface_conversions">type assertion</a> like: </p> <pre> @@ -2560,7 +2602,7 @@ One place this situation arises is when it is necessary to guarantee within the it actually satisfies the interface. If a type—for example, <code><a href="/pkg/encoding/json/#RawMessage">json.RawMessage</a></code>—needs -a custom its JSON representation, it should implement +a custom JSON representation, it should implement <code>json.Marshaler</code>, but there are no static conversions that would cause the compiler to verify this automatically. If the type inadvertently fails to satisfy the interface, the JSON encoder will still work, @@ -2912,7 +2954,7 @@ func handle(r *Request) { } func init() { - for i := 0; i < MaxOutstanding; i++ { + for i := 0; i < MaxOutstanding; i++ { sem <- 1 } } @@ -2940,6 +2982,7 @@ of them can run at any moment. As a result, the program can consume unlimited resources if the requests come in too fast. We can address that deficiency by changing <code>Serve</code> to gate the creation of the goroutines. +Here's an obvious solution, but beware it has a bug we'll fix subsequently: </p> <pre> @@ -2947,6 +2990,46 @@ func Serve(queue chan *Request) { for req := range queue { <-sem go func() { + process(req) // Buggy; see explanation below. + sem <- 1 + }() + } +}</pre> + +<p> +The bug is that in a Go <code>for</code> loop, the loop variable +is reused for each iteration, so the <code>req</code> +variable is shared across all goroutines. +That's not what we want. +We need to make sure that <code>req</code> is unique for each goroutine. +Here's one way to do that, passing the value of <code>req</code> as an argument +to the closure in the goroutine: +</p> + +<pre> +func Serve(queue chan *Request) { + for req := range queue { + <-sem + go func(req *Request) { + process(req) + sem <- 1 + }(req) + } +}</pre> + +<p> +Compare this version with the previous to see the difference in how +the closure is declared and run. +Another solution is just to create a new variable with the same +name, as in this example: +</p> + +<pre> +func Serve(queue chan *Request) { + for req := range queue { + <-sem + req := req // Create new instance of req for the goroutine. + go func() { process(req) sem <- 1 }() @@ -2954,7 +3037,22 @@ func Serve(queue chan *Request) { }</pre> <p> -Another solution that manages resources well is to start a fixed +It may seem odd to write +</p> + +<pre> +req := req +</pre> + +<p> +but it's a legal and idiomatic in Go to do this. +You get a fresh version of the variable with the same name, deliberately +shadowing the loop variable locally but unique to each goroutine. +</p> + +<p> +Going back to the general problem of writing the server, +another approach that manages resources well is to start a fixed number of <code>handle</code> goroutines all reading from the request channel. The number of goroutines limits the number of simultaneous @@ -3254,7 +3352,7 @@ for try := 0; try < 2; try++ { </pre> <p> -The second <code>if</code> statement here is another <a href="#interface_conversion">type assertion</a>. +The second <code>if</code> statement here is another <a href="#interface_conversions">type assertion</a>. If it fails, <code>ok</code> will be false, and <code>e</code> will be <code>nil</code>. If it succeeds, <code>ok</code> will be true, which means the @@ -3437,7 +3535,7 @@ the parse stack by hand: </p> <pre> -if pos==0 { +if pos == 0 { re.error("'*' illegal at start of expression") } </pre> |