mirror of
https://github.com/golang/go
synced 2024-11-21 22:24:40 -07:00
type switches
errors R=rsc DELTA=150 (74 added, 34 deleted, 42 changed) OCL=35647 CL=35650
This commit is contained in:
parent
4700ded282
commit
d2228692b2
@ -628,6 +628,28 @@ func Compare(a, b []byte) int {
|
|||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
A switch can also be used to discover the dynamic type of an interface
|
||||||
|
variable. Such a <em>type switch</em> uses the syntax of a type
|
||||||
|
assertion with the keyword <code>type</code> inside the parentheses.
|
||||||
|
If the switch declares a variable in the expression, the variable will
|
||||||
|
have the corresponding type in each clause.
|
||||||
|
</p>
|
||||||
|
<pre>
|
||||||
|
switch t := interfaceValue.(type) {
|
||||||
|
default:
|
||||||
|
fmt.Printf("unexpected type");
|
||||||
|
case bool:
|
||||||
|
fmt.Printf("boolean %t\n", t);
|
||||||
|
case int:
|
||||||
|
fmt.Printf("integer %d\n", t);
|
||||||
|
case *bool:
|
||||||
|
fmt.Printf("pointer to boolean %t\n", *t);
|
||||||
|
case *int:
|
||||||
|
fmt.Printf("pointer to integer %d\n", *t);
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
<h2 id="functions">Functions</h2>
|
<h2 id="functions">Functions</h2>
|
||||||
|
|
||||||
<h3 id="multiple-returns">Multiple return values</h3>
|
<h3 id="multiple-returns">Multiple return values</h3>
|
||||||
@ -1350,101 +1372,9 @@ By the way, the idea of using <code>Write</code> on a slice of bytes
|
|||||||
is implemented by <code>bytes.Buffer</code>.
|
is implemented by <code>bytes.Buffer</code>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>More to come</h2>
|
<h2>Interfaces</h2>
|
||||||
|
|
||||||
<!---
|
<!---
|
||||||
|
|
||||||
<h2 id="idioms">Idioms</h2>
|
|
||||||
|
|
||||||
|
|
||||||
<h3 id="buffer-slice">Use parallel assignment to slice a buffer</h3>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
header, body, checksum := buf[0:20], buf[20:n-4], buf[n-4:n];
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<h2 id="errors">Errors</h2>
|
|
||||||
|
|
||||||
<h3 id="error-returns">Return <code>os.Error</code>, not <code>bool</code></h3>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Especially in libraries, functions tend to have multiple error modes.
|
|
||||||
Instead of returning a boolean to signal success,
|
|
||||||
return an <code>os.Error</code> that describes the failure.
|
|
||||||
Even if there is only one failure mode now,
|
|
||||||
there may be more later.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3 id="error-context">Return structured errors</h3>
|
|
||||||
|
|
||||||
Implementations of <code>os.Error</code> should
|
|
||||||
describe the error and provide context.
|
|
||||||
For example, <code>os.Open</code> returns an <code>os.PathError</code>:
|
|
||||||
|
|
||||||
<a href="http://go/godoc/src/pkg/os/file.go">http://go/godoc/src/pkg/os/file.go</a>:
|
|
||||||
<pre>
|
|
||||||
// PathError records an error and the operation and
|
|
||||||
// file path that caused it.
|
|
||||||
type PathError struct {
|
|
||||||
Op string;
|
|
||||||
Path string;
|
|
||||||
Error Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *PathError) String() string {
|
|
||||||
return e.Op + " " + e.Path + ": " + e.Error.String();
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<code>PathError</code>'s <code>String</code> formats
|
|
||||||
the error nicely, including the operation and file name
|
|
||||||
tha failed; just printing the error generates a
|
|
||||||
message, such as
|
|
||||||
</p>
|
|
||||||
<pre>
|
|
||||||
open /etc/passwx: no such file or directory
|
|
||||||
</pre>
|
|
||||||
<p>
|
|
||||||
that is useful even if printed far from the call that
|
|
||||||
triggered it.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Callers that care about the precise error details can
|
|
||||||
use a type switch or a type guard to look for specific
|
|
||||||
errors and extract details. For <code>PathErrors</code>
|
|
||||||
this might include examining the internal <code>Error</code>
|
|
||||||
to see if it is <code>os.EPERM</code> or <code>os.ENOENT</code>,
|
|
||||||
for instance.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2 id="types">Programmer-defined types</h2>
|
|
||||||
|
|
||||||
<p>Packages that export only a single type can
|
|
||||||
shorten <code>NewTypeName</code> to <code>New</code>;
|
|
||||||
the vector constructor is
|
|
||||||
<code>vector.New</code>, not <code>vector.NewVector</code>.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
A type that is intended to be allocated
|
|
||||||
as part of a larger struct may have an <code>Init</code> method
|
|
||||||
that must be called explicitly.
|
|
||||||
Conventionally, the <code>Init</code> method returns
|
|
||||||
the object being initialized, to make the constructor trivial:
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<a href="xxx">go/src/pkg/container/vector/vector.go</a>:
|
|
||||||
<pre>
|
|
||||||
func New(len int) *Vector {
|
|
||||||
return new(Vector).Init(len)
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
|
|
||||||
<h2 id="interfaces">Interfaces</h2>
|
|
||||||
|
|
||||||
<h3 id="accept-interface-values">Accept interface values</h3>
|
<h3 id="accept-interface-values">Accept interface values</h3>
|
||||||
|
|
||||||
buffered i/o takes a Reader, not an os.File. XXX
|
buffered i/o takes a Reader, not an os.File. XXX
|
||||||
@ -1473,6 +1403,116 @@ XXX
|
|||||||
<h3 id="fdsa">Use anonymous fields to incorporate an implementation</h3>
|
<h3 id="fdsa">Use anonymous fields to incorporate an implementation</h3>
|
||||||
|
|
||||||
XXX
|
XXX
|
||||||
|
--->
|
||||||
|
|
||||||
|
<h2 id="errors">Errors</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Library routines must often return some sort of error indication to
|
||||||
|
the caller. As mentioned earlier, Go's multivalue return makes it
|
||||||
|
easy to return a detailed error description alongside the normal
|
||||||
|
return value. By convention, errors have type <code>os.Error</code>,
|
||||||
|
a simple interface.
|
||||||
|
</p>
|
||||||
|
<pre>
|
||||||
|
type Error interface {
|
||||||
|
String() string;
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
A library writer is free to implement this interface with a
|
||||||
|
richer model under the covers, making it possible not only
|
||||||
|
to see the error but also to provide some context.
|
||||||
|
For example, <code>os.Open</code> returns an <code>os.PathError</code>.
|
||||||
|
</p>
|
||||||
|
<pre>
|
||||||
|
// PathError records an error and the operation and
|
||||||
|
// file path that caused it.
|
||||||
|
type PathError struct {
|
||||||
|
Op string; // "open", "unlink", etc.
|
||||||
|
Path string; // The associated file.
|
||||||
|
Error Error; // Returned by the system call.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PathError) String() string {
|
||||||
|
return e.Op + " " + e.Path + ": " + e.Error.String();
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
<code>PathError</code>'s <code>String</code> generates
|
||||||
|
a string like this:
|
||||||
|
</p>
|
||||||
|
<pre>
|
||||||
|
open /etc/passwx: no such file or directory
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Such an error, which includes the problematic file name, the
|
||||||
|
operation, and the operating system error it triggered, is useful even
|
||||||
|
if printed far from the call that caused it;
|
||||||
|
it is much more informative than the plain
|
||||||
|
"no such file or directory".
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Callers that care about the precise error details can
|
||||||
|
use a type switch or a type assertion to look for specific
|
||||||
|
errors and extract details. For <code>PathErrors</code>
|
||||||
|
this might include examining the internal <code>Error</code>
|
||||||
|
field for recoverable failures.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
for try := 0; try < 2; try++ {
|
||||||
|
file, err := os.Open(filename, os.O_RDONLY, 0);
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e, ok := err.(*os.PathError); ok && e.Error == os.ENOSPC {
|
||||||
|
deleteTempFiles(); // Recover some space.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h2>Testing</h2>
|
||||||
|
|
||||||
|
<h2>More to come</h2>
|
||||||
|
|
||||||
|
<!---
|
||||||
|
|
||||||
|
<h2 id="idioms">Idioms</h2>
|
||||||
|
|
||||||
|
|
||||||
|
<h3 id="buffer-slice">Use parallel assignment to slice a buffer</h3>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
header, body, checksum := buf[0:20], buf[20:n-4], buf[n-4:n];
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h2 id="types">Programmer-defined types</h2>
|
||||||
|
|
||||||
|
<p>Packages that export only a single type can
|
||||||
|
shorten <code>NewTypeName</code> to <code>New</code>;
|
||||||
|
the vector constructor is
|
||||||
|
<code>vector.New</code>, not <code>vector.NewVector</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
A type that is intended to be allocated
|
||||||
|
as part of a larger struct may have an <code>Init</code> method
|
||||||
|
that must be called explicitly.
|
||||||
|
Conventionally, the <code>Init</code> method returns
|
||||||
|
the object being initialized, to make the constructor trivial:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<a href="xxx">go/src/pkg/container/vector/vector.go</a>:
|
||||||
|
<pre>
|
||||||
|
func New(len int) *Vector {
|
||||||
|
return new(Vector).Init(len)
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
|
||||||
<h2>Data-Driven Programming</h2>
|
<h2>Data-Driven Programming</h2>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user