1
0
mirror of https://github.com/golang/go synced 2024-11-21 14:14:40 -07:00

tutorial: modernize the definition and use of Open.

R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/4446053
This commit is contained in:
Rob Pike 2011-04-18 10:51:40 -07:00
parent beb64bbd6e
commit 23fc9c84bd
6 changed files with 111 additions and 51 deletions

View File

@ -474,8 +474,8 @@ assigned to a variable.
<p> <p>
<h2>An I/O Package</h2> <h2>An I/O Package</h2>
<p> <p>
Next we'll look at a simple package for doing file I/O with the usual Next we'll look at a simple package for doing file I/O with an
sort of open/close/read/write interface. Here's the start of <code>file.go</code>: open/close/read/write interface. Here's the start of <code>file.go</code>:
<p> <p>
<pre> <!-- progs/file.go /package/ /^}/ --> <pre> <!-- progs/file.go /package/ /^}/ -->
05 package file 05 package file
@ -554,10 +554,10 @@ We can use the factory to construct some familiar, exported variables of type <c
</pre> </pre>
<p> <p>
The <code>newFile</code> function was not exported because it's internal. The proper, The <code>newFile</code> function was not exported because it's internal. The proper,
exported factory to use is <code>Open</code>: exported factory to use is <code>OpenFile</code> (we'll explain that name in a moment):
<p> <p>
<pre> <!-- progs/file.go /func.Open/ /^}/ --> <pre> <!-- progs/file.go /func.OpenFile/ /^}/ -->
30 func Open(name string, mode int, perm uint32) (file *File, err os.Error) { 30 func OpenFile(name string, mode int, perm uint32) (file *File, err os.Error) {
31 r, e := syscall.Open(name, mode, perm) 31 r, e := syscall.Open(name, mode, perm)
32 if e != 0 { 32 if e != 0 {
33 err = os.Errno(e) 33 err = os.Errno(e)
@ -566,7 +566,7 @@ exported factory to use is <code>Open</code>:
36 } 36 }
</pre> </pre>
<p> <p>
There are a number of new things in these few lines. First, <code>Open</code> returns There are a number of new things in these few lines. First, <code>OpenFile</code> returns
multiple values, a <code>File</code> and an error (more about errors in a moment). multiple values, a <code>File</code> and an error (more about errors in a moment).
We declare the We declare the
multi-value return as a parenthesized list of declarations; syntactically multi-value return as a parenthesized list of declarations; syntactically
@ -585,6 +585,35 @@ consistent error handling throughout Go code. In <code>Open</code> we use a
conversion to translate Unix's integer <code>errno</code> value into the integer type conversion to translate Unix's integer <code>errno</code> value into the integer type
<code>os.Errno</code>, which implements <code>os.Error</code>. <code>os.Errno</code>, which implements <code>os.Error</code>.
<p> <p>
Why <code>OpenFile</code> and not <code>Open</code>? To mimic Go's <code>os</code> package, which
our exercise is emulating. The <code>os</code> package takes the opportunity
to make the two commonest cases - open for read and create for
write - the simplest, just <code>Open</code> and <code>Create</code>. <code>OpenFile</code> is the
general case, analogous to the Unix system call <code>Open</code>. Here is
the implementation of our <code>Open</code> and <code>Create</code>; they're trivial
wrappers that eliminate common errors by capturing
the tricky standard arguments to open and, especially, to create a file:
<p>
<pre> <!-- progs/file.go /^const/ /^}/ -->
38 const (
39 O_RDONLY = syscall.O_RDONLY
40 O_RDWR = syscall.O_RDWR
41 O_CREATE = syscall.O_CREAT
42 O_TRUNC = syscall.O_TRUNC
43 )
<p>
45 func Open(name string) (file *File, err os.Error) {
46 return OpenFile(name, O_RDONLY, 0)
47 }
</pre>
<p>
<pre> <!-- progs/file.go /func.Create/ /^}/ -->
49 func Create(name string) (file *File, err os.Error) {
50 return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
51 }
</pre>
<p>
Back to our main story.
Now that we can build <code>Files</code>, we can write methods for them. To declare Now that we can build <code>Files</code>, we can write methods for them. To declare
a method of a type, we define a function to have an explicit receiver a method of a type, we define a function to have an explicit receiver
of that type, placed of that type, placed
@ -592,43 +621,43 @@ in parentheses before the function name. Here are some methods for <code>*File</
each of which declares a receiver variable <code>file</code>. each of which declares a receiver variable <code>file</code>.
<p> <p>
<pre> <!-- progs/file.go /Close/ END --> <pre> <!-- progs/file.go /Close/ END -->
38 func (file *File) Close() os.Error { 53 func (file *File) Close() os.Error {
39 if file == nil { 54 if file == nil {
40 return os.EINVAL 55 return os.EINVAL
41 } 56 }
42 e := syscall.Close(file.fd) 57 e := syscall.Close(file.fd)
43 file.fd = -1 // so it can't be closed again 58 file.fd = -1 // so it can't be closed again
44 if e != 0 { 59 if e != 0 {
45 return os.Errno(e) 60 return os.Errno(e)
46 } 61 }
47 return nil 62 return nil
48 } 63 }
<p> <p>
50 func (file *File) Read(b []byte) (ret int, err os.Error) { 65 func (file *File) Read(b []byte) (ret int, err os.Error) {
51 if file == nil { 66 if file == nil {
52 return -1, os.EINVAL 67 return -1, os.EINVAL
53 }
54 r, e := syscall.Read(file.fd, b)
55 if e != 0 {
56 err = os.Errno(e)
57 }
58 return int(r), err
59 }
<p>
61 func (file *File) Write(b []byte) (ret int, err os.Error) {
62 if file == nil {
63 return -1, os.EINVAL
64 }
65 r, e := syscall.Write(file.fd, b)
66 if e != 0 {
67 err = os.Errno(e)
68 } 68 }
69 return int(r), err 69 r, e := syscall.Read(file.fd, b)
70 } 70 if e != 0 {
<p> 71 err = os.Errno(e)
72 func (file *File) String() string { 72 }
73 return file.name 73 return int(r), err
74 } 74 }
<p>
76 func (file *File) Write(b []byte) (ret int, err os.Error) {
77 if file == nil {
78 return -1, os.EINVAL
79 }
80 r, e := syscall.Write(file.fd, b)
81 if e != 0 {
82 err = os.Errno(e)
83 }
84 return int(r), err
85 }
<p>
87 func (file *File) String() string {
88 return file.name
89 }
</pre> </pre>
<p> <p>
There is no implicit <code>this</code> and the receiver variable must be used to access There is no implicit <code>this</code> and the receiver variable must be used to access
@ -658,7 +687,7 @@ We can now use our new package:
13 func main() { 13 func main() {
14 hello := []byte(&quot;hello, world\n&quot;) 14 hello := []byte(&quot;hello, world\n&quot;)
15 file.Stdout.Write(hello) 15 file.Stdout.Write(hello)
16 f, err := file.Open(&quot;/does/not/exist&quot;, 0, 0) 16 f, err := file.Open(&quot;/does/not/exist&quot;)
17 if f == nil { 17 if f == nil {
18 fmt.Printf(&quot;can't open file; err=%s\n&quot;, err.String()) 18 fmt.Printf(&quot;can't open file; err=%s\n&quot;, err.String())
19 os.Exit(1) 19 os.Exit(1)
@ -723,7 +752,7 @@ Building on the <code>file</code> package, here's a simple version of the Unix u
35 cat(file.Stdin) 35 cat(file.Stdin)
36 } 36 }
37 for i := 0; i &lt; flag.NArg(); i++ { 37 for i := 0; i &lt; flag.NArg(); i++ {
38 f, err := file.Open(flag.Arg(i), 0, 0) 38 f, err := file.Open(flag.Arg(i))
39 if f == nil { 39 if f == nil {
40 fmt.Fprintf(os.Stderr, &quot;cat: can't open %s: error %s\n&quot;, flag.Arg(i), err) 40 fmt.Fprintf(os.Stderr, &quot;cat: can't open %s: error %s\n&quot;, flag.Arg(i), err)
41 os.Exit(1) 41 os.Exit(1)

View File

@ -384,8 +384,8 @@ assigned to a variable.
An I/O Package An I/O Package
---- ----
Next we'll look at a simple package for doing file I/O with the usual Next we'll look at a simple package for doing file I/O with an
sort of open/close/read/write interface. Here's the start of "file.go": open/close/read/write interface. Here's the start of "file.go":
--PROG progs/file.go /package/ /^}/ --PROG progs/file.go /package/ /^}/
@ -437,11 +437,11 @@ We can use the factory to construct some familiar, exported variables of type "*
--PROG progs/file.go /var/ /^.$/ --PROG progs/file.go /var/ /^.$/
The "newFile" function was not exported because it's internal. The proper, The "newFile" function was not exported because it's internal. The proper,
exported factory to use is "Open": exported factory to use is "OpenFile" (we'll explain that name in a moment):
--PROG progs/file.go /func.Open/ /^}/ --PROG progs/file.go /func.OpenFile/ /^}/
There are a number of new things in these few lines. First, "Open" returns There are a number of new things in these few lines. First, "OpenFile" returns
multiple values, a "File" and an error (more about errors in a moment). multiple values, a "File" and an error (more about errors in a moment).
We declare the We declare the
multi-value return as a parenthesized list of declarations; syntactically multi-value return as a parenthesized list of declarations; syntactically
@ -460,6 +460,20 @@ consistent error handling throughout Go code. In "Open" we use a
conversion to translate Unix's integer "errno" value into the integer type conversion to translate Unix's integer "errno" value into the integer type
"os.Errno", which implements "os.Error". "os.Errno", which implements "os.Error".
Why "OpenFile" and not "Open"? To mimic Go's "os" package, which
our exercise is emulating. The "os" package takes the opportunity
to make the two commonest cases - open for read and create for
write - the simplest, just "Open" and "Create". "OpenFile" is the
general case, analogous to the Unix system call "Open". Here is
the implementation of our "Open" and "Create"; they're trivial
wrappers that eliminate common errors by capturing
the tricky standard arguments to open and, especially, to create a file:
--PROG progs/file.go /^const/ /^}/
--PROG progs/file.go /func.Create/ /^}/
Back to our main story.
Now that we can build "Files", we can write methods for them. To declare Now that we can build "Files", we can write methods for them. To declare
a method of a type, we define a function to have an explicit receiver a method of a type, we define a function to have an explicit receiver
of that type, placed of that type, placed

View File

@ -24,6 +24,7 @@ func cat(f *file.File) {
case nr > 0: case nr > 0:
if nw, ew := file.Stdout.Write(buf[0:nr]); nw != nr { if nw, ew := file.Stdout.Write(buf[0:nr]); nw != nr {
fmt.Fprintf(os.Stderr, "cat: error writing from %s: %s\n", f.String(), ew.String()) fmt.Fprintf(os.Stderr, "cat: error writing from %s: %s\n", f.String(), ew.String())
os.Exit(1)
} }
} }
} }
@ -35,7 +36,7 @@ func main() {
cat(file.Stdin) cat(file.Stdin)
} }
for i := 0; i < flag.NArg(); i++ { for i := 0; i < flag.NArg(); i++ {
f, err := file.Open(flag.Arg(i), 0, 0) f, err := file.Open(flag.Arg(i))
if f == nil { if f == nil {
fmt.Fprintf(os.Stderr, "cat: can't open %s: error %s\n", flag.Arg(i), err) fmt.Fprintf(os.Stderr, "cat: can't open %s: error %s\n", flag.Arg(i), err)
os.Exit(1) os.Exit(1)

View File

@ -67,6 +67,7 @@ func cat(r reader) {
nw, ew := file.Stdout.Write(buf[0:nr]) nw, ew := file.Stdout.Write(buf[0:nr])
if nw != nr { if nw != nr {
fmt.Fprintf(os.Stderr, "cat: error writing from %s: %s\n", r.String(), ew.String()) fmt.Fprintf(os.Stderr, "cat: error writing from %s: %s\n", r.String(), ew.String())
os.Exit(1)
} }
} }
} }
@ -78,7 +79,7 @@ func main() {
cat(file.Stdin) cat(file.Stdin)
} }
for i := 0; i < flag.NArg(); i++ { for i := 0; i < flag.NArg(); i++ {
f, err := file.Open(flag.Arg(i), 0, 0) f, err := file.Open(flag.Arg(i))
if f == nil { if f == nil {
fmt.Fprintf(os.Stderr, "cat: can't open %s: error %s\n", flag.Arg(i), err) fmt.Fprintf(os.Stderr, "cat: can't open %s: error %s\n", flag.Arg(i), err)
os.Exit(1) os.Exit(1)

View File

@ -27,7 +27,7 @@ var (
Stderr = newFile(syscall.Stderr, "/dev/stderr") Stderr = newFile(syscall.Stderr, "/dev/stderr")
) )
func Open(name string, mode int, perm uint32) (file *File, err os.Error) { func OpenFile(name string, mode int, perm uint32) (file *File, err os.Error) {
r, e := syscall.Open(name, mode, perm) r, e := syscall.Open(name, mode, perm)
if e != 0 { if e != 0 {
err = os.Errno(e) err = os.Errno(e)
@ -35,6 +35,21 @@ func Open(name string, mode int, perm uint32) (file *File, err os.Error) {
return newFile(r, name), err return newFile(r, name), err
} }
const (
O_RDONLY = syscall.O_RDONLY
O_RDWR = syscall.O_RDWR
O_CREATE = syscall.O_CREAT
O_TRUNC = syscall.O_TRUNC
)
func Open(name string) (file *File, err os.Error) {
return OpenFile(name, O_RDONLY, 0)
}
func Create(name string) (file *File, err os.Error) {
return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}
func (file *File) Close() os.Error { func (file *File) Close() os.Error {
if file == nil { if file == nil {
return os.EINVAL return os.EINVAL

View File

@ -13,7 +13,7 @@ import (
func main() { func main() {
hello := []byte("hello, world\n") hello := []byte("hello, world\n")
file.Stdout.Write(hello) file.Stdout.Write(hello)
f, err := file.Open("/does/not/exist", 0, 0) f, err := file.Open("/does/not/exist")
if f == nil { if f == nil {
fmt.Printf("can't open file; err=%s\n", err.String()) fmt.Printf("can't open file; err=%s\n", err.String())
os.Exit(1) os.Exit(1)