mirror of
https://github.com/golang/go
synced 2024-11-21 18:34:44 -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:
parent
beb64bbd6e
commit
23fc9c84bd
@ -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("hello, world\n")
|
14 hello := []byte("hello, world\n")
|
||||||
15 file.Stdout.Write(hello)
|
15 file.Stdout.Write(hello)
|
||||||
16 f, err := file.Open("/does/not/exist", 0, 0)
|
16 f, err := file.Open("/does/not/exist")
|
||||||
17 if f == nil {
|
17 if f == nil {
|
||||||
18 fmt.Printf("can't open file; err=%s\n", err.String())
|
18 fmt.Printf("can't open file; err=%s\n", 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 < flag.NArg(); i++ {
|
37 for i := 0; i < 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, "cat: can't open %s: error %s\n", flag.Arg(i), err)
|
40 fmt.Fprintf(os.Stderr, "cat: can't open %s: error %s\n", flag.Arg(i), err)
|
||||||
41 os.Exit(1)
|
41 os.Exit(1)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user