CSI2120 Programming Paradigms Jochen Lang
jlang@uottawa.ca
Faculté de génie | Faculty of Engineering
Jochen Lang, EECS jlang@uOttawa.ca
System Programming: Go
• Stream I/O
– Console I/O
– File I/O
• Panic and Recover
• Methods
• Interfaces
• Embedded Types
Jochen Lang, EECS jlang@uOttawa.ca
Console I/O
• Console I/O with package fmt. C-like syntax with scanf and printf in addition to print (println).
package main
import “fmt”
func main() {
var inStr string
fmt.Printf(“Input? “)
fmt.Scanf(“%s”, &inStr)
fmt.Printf(“\nOutput: %s\n”, inStr)
}
Jochen Lang, EECS jlang@uOttawa.ca
File I/O
• File I/O follows familiar notion of stream I/O
• Relevant packages are os and bufio besides fmt
func fCopy(outN, inN string) (bytesOut int64, err
error) {
inF, err := os.Open(inN)
if err != nil {
return }
outF, err := os.Create(outN)
if err != nil {
inF.Close()
return }
bytesOut, err = io.Copy(outF, inF)
inF.Close(); outF.Close()
return
}
Jochen Lang, EECS jlang@uOttawa.ca
Final Evaluation with defer
• With defer the execution of a statement can be
deferred to the end of a function or block.
• Convenient for cleaning up at the end of multiple control path through a function.
– E.g., file handles
• The parameters of a delayed function are evaluated at the issue of the defer statement.
• The execution of a deferred function takes place even if the function is exited with an error
Jochen Lang, EECS jlang@uOttawa.ca
File Copy with Deferred Clean-up
• Defer simplifies error handling
func fCopy(outN, inN string) (bytesOut int64, err
error) {
inF, err := os.Open(inN)
if err != nil {
return }
defer inF.Close()
outF, err := os.Create(outN)
if err != nil {
return }
defer outF.Close()
bytesOut, err = io.Copy(outF, inF)
return
}
Jochen Lang, EECS jlang@uOttawa.ca
Errors and Panic in Go
• No exceptions in Go
• Instead of exception return error codes of type error
– Convention: On success error is nil
• When a serious error occurs, use the panic statement
– Only to be used for unforeseen errors
– Corresponds to violations of assertions (C assert statement)
• In case of panic:
– function stops immediately
– deferred functions are executed – stack unwinding occurs
• return to the calling functions
• executing their deferred functions until main exits • can be stopped with recover
Jochen Lang, EECS jlang@uOttawa.ca
Panic Toy Example
package main
import “fmt”
func main() {
defer fmt.Println(“Last Output”)
fmt.Println(“Before Panic” )
panic(“Out of here”)
fmt.Println(“After Panic”)
}
Before Panic
Last Output
panic: Out of here
goroutine 1 [running]:
runtime.panic(0x48d4e0, 0xc084005260)
C:/Users/ADMINI~1/AppData/Local/Temp/2/makerelease2 50988475/go/src/pkg/runtime/panic.c:266 +0xc8 main.main()
c:/teaching/CSI2120/demoCode/go/panictoy.go:8 +0x18e
Jochen Lang, EECS jlang@uOttawa.ca
Panic and Recover
package main
import “fmt”
func causePanic() {
panic(“Caused panic”)
}
func foo() {
defer func() {
if err := recover(); err != nil {
fmt.Printf(“Recovered from >> %s <<\n", err)
} }()
causePanic()
fmt.Println("Regular after panic") // not shown
}
func main() {
}
fmt.Println("In main:")
foo()
fmt.Println("End main:")
Jochen Lang, EECS jlang@uOttawa.ca
Example Run Recover and Go
• Program terminates regularly
• Note that function foo is exited through a deferred function call
• Output:
In main:
Recovered from >> Caused panic <<
End main:
Jochen Lang, EECS jlang@uOttawa.ca
Methods and Receivers
• A method is a function acts on a certain type (its receiver)
• Receiver type can be almost anything
– But no interfaces, no pointer type (but a pointer to an allowed type)
– Often a structure is the receiver
• The structure and methods are not grouped together
– they only have to be in the same package (but can be in different source files)
– no encapsulation as with classes
• No method overloading (just as with functions)
Jochen Lang, EECS jlang@uOttawa.ca
Definition of and Calling a Method
package main
import (
"fmt" ) "math"
type Point struct {
x float64
} y float64
func (pt *Point) norm() float64 {
} return math.Sqrt(pt.x*pt.x + pt.y*pt.y)
func main() {
a := Point{2.,4.}
}
n := a.norm()
fmt.Printf("2-Norm = %f\n", n)
Jochen Lang, EECS jlang@uOttawa.ca
Interfaces
• Interfaces define a set of methods – no code for the method
– abstract definitions
• Naming convention
– Interface name should end in “er”, e.g., Writer, Reader, Logger etc.
• Implementing an interface
– A type does not need to state that implements an interface
– A type can implement multiple interfaces
– Interface can embed other interfaces
– Interfaces can be assigned to variables
Jochen Lang, EECS jlang@uOttawa.ca
Interface example
type ColorPt struct {
pt Point
color string
}
type Box struct {
weight float64
color string
}
// interface implemented by ColorPt and Box
type Color interface {
}
SetColor(string)
Color() string
Jochen Lang, EECS jlang@uOttawa.ca
Implementation of an Interface
• Interface implementation for type ColorPt func (p *ColorPt) Color() string {
return p.color
}
func (p *ColorPt) SetColor(col string) {
p.color = col
}
• Interface implementation for type Box func (p *Box) SetColor(col string) {
p.color = col
}
func (p *Box) Color() string {
return p.color
}
Jochen Lang, EECS jlang@uOttawa.ca
Polymorphism with Interfaces
• Use an array of pointers to structures implementing the Color interface
func main() {
table := [...]Color{
&ColorPt{Point{1.1,2.2},"red"},
&Box{32.4, "yellow"},
&ColorPt{Point{3.1,1.2},"blue"}}
for _,element := range table {
fmt.Printf("color= %s\n",
} }
element.Color())
Jochen Lang, EECS jlang@uOttawa.ca
Embedded Types
• We can embed a type in a structure
• The new type contains the embedded type (this is not inheritance but can be used similarly)
type Person struct {
lastName string
firstName string
}
type Student struct {
Person
studentId int
}
func (p *Person) print() {
fmt.Printf("%s, %s", p.lastName, p.firstName)
}
func (s *Student) print() {
s.Person.print()
fmt.Printf(": %d ", s.studentId)
}
Jochen Lang, EECS jlang@uOttawa.ca
Calling Methods for the Embedded Type
• The new structure containing the embedded type can be a receiver for methods of the embedded type
type Person struct {
lastName string
firstName string
dob time.Time
}
func main() {
s := Student{Person{"Joe", "Smith",
time.Date(1995,4,7,0,0,0,0,time.UTC)}, 101 } s.print() // print method with student receiver s.age() // age method with person receiver
}
func (p *Person) age() {
... // omitted here
}
Jochen Lang, EECS jlang@uOttawa.ca
Summary
• Stream I/O
– Console I/O
– File I/O
• Panic and Recover
• Methods
• Interfaces
• Embedded types
Jochen Lang, EECS jlang@uOttawa.ca