Commit e9f4fb28 authored by Rob Pike's avatar Rob Pike

change the tutorial to use File, file rather than FD, fd.

also make the default input for makehtml be go_tutorial.txt.

R=rsc
DELTA=176  (58 added, 58 deleted, 60 changed)
OCL=26374
CL=26374
parent bd64e810
...@@ -4,7 +4,7 @@ Let's Go ...@@ -4,7 +4,7 @@ Let's Go
Rob Pike Rob Pike
---- ----
(February 4, 2009) (March 17, 2009)
This document is a tutorial introduction to the basics of the Go systems programming This document is a tutorial introduction to the basics of the Go systems programming
...@@ -51,7 +51,7 @@ program that doesn't depend on "print()": ...@@ -51,7 +51,7 @@ program that doesn't depend on "print()":
--PROG progs/helloworld2.go --PROG progs/helloworld2.go
This version imports the ''os'' package to acess its "Stdout" variable, of type This version imports the ''os'' package to acess its "Stdout" variable, of type
"*os.FD". The "import" statement is a declaration: it names the identifier ("os") "*os.File". The "import" statement is a declaration: it names the identifier ("os")
that will be used to access members of the package imported from the file ("os"), that will be used to access members of the package imported from the file ("os"),
found in the current directory or in a standard location. found in the current directory or in a standard location.
Given "os.Stdout" we can use its "WriteString" method to print the string. Given "os.Stdout" we can use its "WriteString" method to print the string.
...@@ -307,11 +307,11 @@ An I/O Package ...@@ -307,11 +307,11 @@ 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 the usual
sort of open/close/read/write interface. Here's the start of "fd.go": sort of open/close/read/write interface. Here's the start of "file.go":
--PROG progs/fd.go /package/ /^}/ --PROG progs/file.go /package/ /^}/
The first line declares the name of the package -- "fd" for ''file descriptor'' -- The first line declares the name of the package -- "file" --
and then we import two packages. The "os" package hides the differences and then we import two packages. The "os" package hides the differences
between various operating systems to give a consistent view of files and between various operating systems to give a consistent view of files and
so on; here we're only going to use its error handling utilities so on; here we're only going to use its error handling utilities
...@@ -321,11 +321,11 @@ The other item is the low-level, external "syscall" package, which provides ...@@ -321,11 +321,11 @@ The other item is the low-level, external "syscall" package, which provides
a primitive interface to the underlying operating system's calls. a primitive interface to the underlying operating system's calls.
Next is a type definition: the "type" keyword introduces a type declaration, Next is a type definition: the "type" keyword introduces a type declaration,
in this case a data structure called "FD". in this case a data structure called "File".
To make things a little more interesting, our "FD" includes the name of the file To make things a little more interesting, our "File" includes the name of the file
that the file descriptor refers to. that the file descriptor refers to.
Because "FD" starts with a capital letter, the type is available outside the package, Because "File" starts with a capital letter, the type is available outside the package,
that is, by users of the package. In Go the rule about visibility of information is that is, by users of the package. In Go the rule about visibility of information is
simple: if a name (of a top-level type, function, method, constant, variable, or of simple: if a name (of a top-level type, function, method, constant, variable, or of
a structure field) is capitalized, users of the package may see it. Otherwise, the a structure field) is capitalized, users of the package may see it. Otherwise, the
...@@ -333,37 +333,37 @@ name and hence the thing being named is visible only inside the package in which ...@@ -333,37 +333,37 @@ name and hence the thing being named is visible only inside the package in which
it is declared. This is more than a convention; the rule is enforced by the compiler. it is declared. This is more than a convention; the rule is enforced by the compiler.
In Go, the term for publicly visible names is ''exported''. In Go, the term for publicly visible names is ''exported''.
In the case of "FD", all its fields are lower case and so invisible to users, but we In the case of "File", all its fields are lower case and so invisible to users, but we
will soon give it some exported, upper-case methods. will soon give it some exported, upper-case methods.
First, though, here is a factory to create them: First, though, here is a factory to create them:
--PROG progs/fd.go /newFD/ /^}/ --PROG progs/file.go /newFile/ /^}/
This returns a pointer to a new "FD" structure with the file descriptor and name This returns a pointer to a new "File" structure with the file descriptor and name
filled in. This code uses Go's notion of a ''composite literal'', analogous to filled in. This code uses Go's notion of a ''composite literal'', analogous to
the ones used to build maps and arrays, to construct a new heap-allocated the ones used to build maps and arrays, to construct a new heap-allocated
object. We could write object. We could write
n := new(FD); n := new(File);
n.fildes = fd; n.fd = fd;
n.name = name; n.name = name;
return n return n
but for simple structures like "FD" it's easier to return the address of a nonce but for simple structures like "File" it's easier to return the address of a nonce
composite literal, as is done here on line 17. composite literal, as is done here on line 17.
We can use the factory to construct some familiar, exported variables of type "*FD": We can use the factory to construct some familiar, exported variables of type "*File":
--PROG progs/fd.go /var/ /^.$/ --PROG progs/file.go /var/ /^.$/
The "newFD" 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 "Open":
--PROG progs/fd.go /func.Open/ /^}/ --PROG progs/file.go /func.Open/ /^}/
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, "Open" returns
multiple values, an "FD" and an error (more about errors in a moment). multiple values, an "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
they look just like a second parameter list. The function they look just like a second parameter list. The function
...@@ -371,9 +371,9 @@ they look just like a second parameter list. The function ...@@ -371,9 +371,9 @@ they look just like a second parameter list. The function
also has a multi-value return, which we can grab with the multi-variable also has a multi-value return, which we can grab with the multi-variable
declaration on line 27; it declares "r" and "e" to hold the two values, declaration on line 27; it declares "r" and "e" to hold the two values,
both of type "int64" (although you'd have to look at the "syscall" package both of type "int64" (although you'd have to look at the "syscall" package
to see that). Finally, line 28 returns two values: a pointer to the new "FD" to see that). Finally, line 28 returns two values: a pointer to the new "File"
and the error. If "syscall.Open" fails, the file descriptor "r" will and the error. If "syscall.Open" fails, the file descriptor "r" will
be negative and "NewFD" will return "nil". be negative and "NewFile" will return "nil".
About those errors: The "os" library includes a general notion of an error About those errors: The "os" library includes a general notion of an error
string, maintaining a unique set of errors throughout the program. It's a string, maintaining a unique set of errors throughout the program. It's a
...@@ -382,13 +382,13 @@ consistent error handling throughout Go code. In "Open" we use the ...@@ -382,13 +382,13 @@ consistent error handling throughout Go code. In "Open" we use the
routine "os.ErrnoToError" to translate Unix's integer "errno" value into routine "os.ErrnoToError" to translate Unix's integer "errno" value into
an error string, which will be stored in a unique instance of "*os.Error". an error string, which will be stored in a unique instance of "*os.Error".
Now that we can build "FDs", 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
in parentheses before the function name. Here are some methods for "*FD", in parentheses before the function name. Here are some methods for "*File",
each of which declares a receiver variable "fd". each of which declares a receiver variable "file".
--PROG progs/fd.go /Close/ END --PROG progs/file.go /Close/ END
There is no implicit "this" and the receiver variable must be used to access There is no implicit "this" and the receiver variable must be used to access
members of the structure. Methods are not declared within members of the structure. Methods are not declared within
...@@ -417,7 +417,7 @@ and run the program: ...@@ -417,7 +417,7 @@ and run the program:
Rotting cats Rotting cats
---- ----
Building on the "fd" package, here's a simple version of the Unix utility "cat(1)", Building on the "file" package, here's a simple version of the Unix utility "cat(1)",
"progs/cat.go": "progs/cat.go":
--PROG progs/cat.go --PROG progs/cat.go
...@@ -425,7 +425,7 @@ Building on the "fd" package, here's a simple version of the Unix utility "cat(1 ...@@ -425,7 +425,7 @@ Building on the "fd" package, here's a simple version of the Unix utility "cat(1
By now this should be easy to follow, but the "switch" statement introduces some By now this should be easy to follow, but the "switch" statement introduces some
new features. Like a "for" loop, an "if" or "switch" can include an new features. Like a "for" loop, an "if" or "switch" can include an
initialization statement. The "switch" on line 12 uses one to create variables initialization statement. The "switch" on line 12 uses one to create variables
"nr" and "er" to hold the return values from "fd.Read()". (The "if" on line 19 "nr" and "er" to hold the return values from "f.Read()". (The "if" on line 19
has the same idea.) The "switch" statement is general: it evaluates the cases has the same idea.) The "switch" statement is general: it evaluates the cases
from top to bottom looking for the first case that matches the value; the from top to bottom looking for the first case that matches the value; the
case expressions don't need to be constants or even integers, as long as case expressions don't need to be constants or even integers, as long as
...@@ -444,7 +444,7 @@ Now let's make a variant of "cat" that optionally does "rot13" on its input. ...@@ -444,7 +444,7 @@ Now let's make a variant of "cat" that optionally does "rot13" on its input.
It's easy to do by just processing the bytes, but instead we will exploit It's easy to do by just processing the bytes, but instead we will exploit
Go's notion of an <i>interface</i>. Go's notion of an <i>interface</i>.
The "cat()" subroutine uses only two methods of "fd": "Read()" and "String()", The "cat()" subroutine uses only two methods of "f": "Read()" and "String()",
so let's start by defining an interface that has exactly those two methods. so let's start by defining an interface that has exactly those two methods.
Here is code from "progs/cat_rot13.go": Here is code from "progs/cat_rot13.go":
...@@ -452,9 +452,9 @@ Here is code from "progs/cat_rot13.go": ...@@ -452,9 +452,9 @@ Here is code from "progs/cat_rot13.go":
Any type that implements the two methods of "reader" -- regardless of whatever Any type that implements the two methods of "reader" -- regardless of whatever
other methods the type may also contain -- is said to <i>implement</i> the other methods the type may also contain -- is said to <i>implement</i> the
interface. Since "fd.FD" implements these methods, it implements the interface. Since "file.File" implements these methods, it implements the
"reader" interface. We could tweak the "cat" subroutine to accept a "reader" "reader" interface. We could tweak the "cat" subroutine to accept a "reader"
instead of a "*fd.FD" and it would work just fine, but let's embellish a little instead of a "*file.File" and it would work just fine, but let's embellish a little
first by writing a second type that implements "reader", one that wraps an first by writing a second type that implements "reader", one that wraps an
existing "reader" and does "rot13" on the data. To do this, we just define existing "reader" and does "rot13" on the data. To do this, we just define
the type and implement the methods and with no other bookkeeping, the type and implement the methods and with no other bookkeeping,
...@@ -497,7 +497,7 @@ type if the type implements all the methods declared in the interface. ...@@ -497,7 +497,7 @@ type if the type implements all the methods declared in the interface.
This means This means
that a type may implement an arbitrary number of different interfaces. that a type may implement an arbitrary number of different interfaces.
There is no type hierarchy; things can be much more <i>ad hoc</i>, There is no type hierarchy; things can be much more <i>ad hoc</i>,
as we saw with "rot13". The type "fd.FD" implements "reader"; it could also as we saw with "rot13". The type "file.File" implements "reader"; it could also
implement a "writer", or any other interface built from its methods that implement a "writer", or any other interface built from its methods that
fits the current situation. Consider the <i>empty interface</i> fits the current situation. Consider the <i>empty interface</i>
......
...@@ -5,23 +5,23 @@ ...@@ -5,23 +5,23 @@
package main package main
import ( import (
"fd"; "file";
"flag"; "flag";
) )
func cat(file *fd.FD) { func cat(f *file.File) {
const NBUF = 512; const NBUF = 512;
var buf [NBUF]byte; var buf [NBUF]byte;
for { for {
switch nr, er := file.Read(buf); true { switch nr, er := f.Read(buf); true {
case nr < 0: case nr < 0:
print("error reading from ", file.String(), ": ", er.String(), "\n"); print("error reading from ", f.String(), ": ", er.String(), "\n");
sys.Exit(1); sys.Exit(1);
case nr == 0: // EOF case nr == 0: // EOF
return; return;
case nr > 0: case nr > 0:
if nw, ew := fd.Stdout.Write(buf[0:nr]); nw != nr { if nw, ew := file.Stdout.Write(buf[0:nr]); nw != nr {
print("error writing from ", file.String(), ": ", ew.String(), "\n"); print("error writing from ", f.String(), ": ", ew.String(), "\n");
} }
} }
} }
...@@ -30,15 +30,15 @@ func cat(file *fd.FD) { ...@@ -30,15 +30,15 @@ func cat(file *fd.FD) {
func main() { func main() {
flag.Parse(); // Scans the arg list and sets up flags flag.Parse(); // Scans the arg list and sets up flags
if flag.NArg() == 0 { if flag.NArg() == 0 {
cat(fd.Stdin); cat(file.Stdin);
} }
for i := 0; i < flag.NArg(); i++ { for i := 0; i < flag.NArg(); i++ {
file, err := fd.Open(flag.Arg(i), 0, 0); f, err := file.Open(flag.Arg(i), 0, 0);
if file == nil { if f == nil {
print("can't open ", flag.Arg(i), ": error ", err, "\n"); print("can't open ", flag.Arg(i), ": error ", err, "\n");
sys.Exit(1); sys.Exit(1);
} }
cat(file); cat(f);
file.Close(); f.Close();
} }
} }
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
package main package main
import ( import (
"fd"; "file";
"flag"; "flag";
"os"; "os";
) )
...@@ -63,7 +63,7 @@ func cat(r reader) { ...@@ -63,7 +63,7 @@ func cat(r reader) {
case nr == 0: // EOF case nr == 0: // EOF
return; return;
case nr > 0: case nr > 0:
nw, ew := fd.Stdout.Write(buf[0:nr]); nw, ew := file.Stdout.Write(buf[0:nr]);
if nw != nr { if nw != nr {
print("error writing from ", r.String(), ": ", ew.String(), "\n"); print("error writing from ", r.String(), ": ", ew.String(), "\n");
} }
...@@ -74,15 +74,15 @@ func cat(r reader) { ...@@ -74,15 +74,15 @@ func cat(r reader) {
func main() { func main() {
flag.Parse(); // Scans the arg list and sets up flags flag.Parse(); // Scans the arg list and sets up flags
if flag.NArg() == 0 { if flag.NArg() == 0 {
cat(fd.Stdin); cat(file.Stdin);
} }
for i := 0; i < flag.NArg(); i++ { for i := 0; i < flag.NArg(); i++ {
file, err := fd.Open(flag.Arg(i), 0, 0); f, err := file.Open(flag.Arg(i), 0, 0);
if file == nil { if f == nil {
print("can't open ", flag.Arg(i), ": error ", err, "\n"); print("can't open ", flag.Arg(i), ": error ", err, "\n");
sys.Exit(1); sys.Exit(1);
} }
cat(file); cat(f);
file.Close(); f.Close();
} }
} }
...@@ -2,61 +2,61 @@ ...@@ -2,61 +2,61 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package fd package file
import ( import (
"os"; "os";
"syscall"; "syscall";
) )
type FD struct { type File struct {
fildes int64; // file descriptor number fd int64; // file descriptor number
name string; // file name at Open time name string; // file name at Open time
} }
func newFD(fd int64, name string) *FD { func newFile(fd int64, name string) *File {
if fd < 0 { if fd < 0 {
return nil return nil
} }
return &FD{fd, name} return &File{fd, name}
} }
var ( var (
Stdin = newFD(0, "/dev/stdin"); Stdin = newFile(0, "/dev/stdin");
Stdout = newFD(1, "/dev/stdout"); Stdout = newFile(1, "/dev/stdout");
Stderr = newFD(2, "/dev/stderr"); Stderr = newFile(2, "/dev/stderr");
) )
func Open(name string, mode int64, perm int64) (fd *FD, err *os.Error) { func Open(name string, mode int64, perm int64) (file *File, err *os.Error) {
r, e := syscall.Open(name, mode, perm); r, e := syscall.Open(name, mode, perm);
return newFD(r, name), os.ErrnoToError(e) return newFile(r, name), os.ErrnoToError(e)
} }
func (fd *FD) Close() *os.Error { func (file *File) Close() *os.Error {
if fd == nil { if file == nil {
return os.EINVAL return os.EINVAL
} }
r, e := syscall.Close(fd.fildes); r, e := syscall.Close(file.fd);
fd.fildes = -1; // so it can't be closed again file.fd = -1; // so it can't be closed again
return nil return nil
} }
func (fd *FD) Read(b []byte) (ret int, err *os.Error) { func (file *File) Read(b []byte) (ret int, err *os.Error) {
if fd == nil { if file == nil {
return -1, os.EINVAL return -1, os.EINVAL
} }
r, e := syscall.Read(fd.fildes, &b[0], int64(len(b))); r, e := syscall.Read(file.fd, &b[0], int64(len(b)));
return int(r), os.ErrnoToError(e) return int(r), os.ErrnoToError(e)
} }
func (fd *FD) Write(b []byte) (ret int, err *os.Error) { func (file *File) Write(b []byte) (ret int, err *os.Error) {
if fd == nil { if file == nil {
return -1, os.EINVAL return -1, os.EINVAL
} }
r, e := syscall.Write(fd.fildes, &b[0], int64(len(b))); r, e := syscall.Write(file.fd, &b[0], int64(len(b)));
return int(r), os.ErrnoToError(e) return int(r), os.ErrnoToError(e)
} }
func (fd *FD) String() string { func (file *File) String() string {
return fd.name return file.name
} }
...@@ -4,12 +4,12 @@ ...@@ -4,12 +4,12 @@
package main package main
import fd "fd" import file "file"
func main() { func main() {
hello := []byte{'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '\n'}; hello := []byte{'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '\n'};
fd.Stdout.Write(hello); file.Stdout.Write(hello);
file, err := fd.Open("/does/not/exist", 0, 0); file, err := file.Open("/does/not/exist", 0, 0);
if file == nil { if file == nil {
print("can't open file; err=", err.String(), "\n"); print("can't open file; err=", err.String(), "\n");
sys.Exit(1); sys.Exit(1);
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
rm -f *.6 rm -f *.6
for i in \ for i in \
fd.go \ file.go \
helloworld.go \ helloworld.go \
helloworld2.go \ helloworld2.go \
helloworld3.go \ helloworld3.go \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment