forked from steger/pr3-sose2026
added comments
parent
0fd016ebb3
commit
0c79676b86
|
|
@ -5,23 +5,28 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// a function that can fail, so it returns an error as the second return value
|
||||||
func f(arg int) (int, error) {
|
func f(arg int) (int, error) {
|
||||||
if arg == 42 {
|
if arg == 42 {
|
||||||
|
// in case of an error, return the zero value for the result and a non-nil error
|
||||||
return -1, errors.New("can't work with 42")
|
return 0, errors.New("can't work with 42")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// simply return the result and a nil error in the success case
|
||||||
return arg + 3, nil
|
return arg + 3, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// define some errors to use in makeTea
|
||||||
var ErrOutOfTea = fmt.Errorf("no more tea available")
|
var ErrOutOfTea = fmt.Errorf("no more tea available")
|
||||||
var ErrPower = fmt.Errorf("can't boil water")
|
var ErrPower = fmt.Errorf("can't boil water")
|
||||||
|
|
||||||
|
// a function that can fail in multiple ways, so it returns an error as the second return value
|
||||||
func makeTea(arg int) error {
|
func makeTea(arg int) error {
|
||||||
if arg == 2 {
|
if arg == 2 {
|
||||||
|
// return the error directly, so we can check for it later with errors.Is
|
||||||
return ErrOutOfTea
|
return ErrOutOfTea
|
||||||
} else if arg == 4 {
|
} else if arg == 4 {
|
||||||
|
// use %w to wrap the error, so we can check for it later with errors.Is
|
||||||
return fmt.Errorf("making tea: %w", ErrPower)
|
return fmt.Errorf("making tea: %w", ErrPower)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -30,6 +35,7 @@ func makeTea(arg int) error {
|
||||||
func main() {
|
func main() {
|
||||||
for _, i := range []int{7, 42} {
|
for _, i := range []int{7, 42} {
|
||||||
|
|
||||||
|
//typical pattern for checking errors in Go: if the error is not nil, handle it and return or continue; otherwise, use the result
|
||||||
if r, e := f(i); e != nil {
|
if r, e := f(i); e != nil {
|
||||||
fmt.Println("f failed:", e)
|
fmt.Println("f failed:", e)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -40,9 +46,10 @@ func main() {
|
||||||
for i := range 5 {
|
for i := range 5 {
|
||||||
if err := makeTea(i); err != nil {
|
if err := makeTea(i); err != nil {
|
||||||
|
|
||||||
|
// use errors.Is to check for specific errors, even if they are wrapped
|
||||||
if errors.Is(err, ErrOutOfTea) {
|
if errors.Is(err, ErrOutOfTea) {
|
||||||
fmt.Println("We should buy new tea!")
|
fmt.Println("We should buy new tea!")
|
||||||
} else if errors.Is(err, ErrPower) {
|
} else if errors.Is(err, ErrPower) { // this error is wrapped, but errors.Is can still check for it
|
||||||
fmt.Println("Now it is dark.")
|
fmt.Println("Now it is dark.")
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("unknown error: %s\n", err)
|
fmt.Printf("unknown error: %s\n", err)
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,11 @@ package main
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
func intSeq() func() int {
|
// a closure is a function value that references variables from outside its body.
|
||||||
i := 0
|
// The function may access and assign to the referenced variables; in this sense the function is "bound" to the variables.
|
||||||
return func() int {
|
func intSeq() func() int { //returns a function that returns an int
|
||||||
|
i := 0 // i is a variable that intSeq's function value will reference. It continues to exist even after intSeq returns.
|
||||||
|
return func() int { // the anonomous function is returned here. It is not executed yet.
|
||||||
i++
|
i++
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
@ -12,12 +14,15 @@ func intSeq() func() int {
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
|
//functions are first class citizens in Go, so we can assign them to variables,
|
||||||
|
// pass them as arguments to other functions, and return them from functions.
|
||||||
nextInt := intSeq()
|
nextInt := intSeq()
|
||||||
|
|
||||||
fmt.Println(nextInt())
|
fmt.Println(nextInt())
|
||||||
fmt.Println(nextInt())
|
fmt.Println(nextInt())
|
||||||
fmt.Println(nextInt())
|
fmt.Println(nextInt())
|
||||||
|
|
||||||
|
//a second function value from intSeq, with its own i variable.
|
||||||
newInts := intSeq()
|
newInts := intSeq()
|
||||||
fmt.Println(newInts())
|
fmt.Println(newInts())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@ package main
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
|
// a generic function that takes a slice of any type and a value of that type, and returns the index of the value in the slice, or -1 if it is not found.
|
||||||
|
// The type parameters S and E are declared in square brackets before the function name, and they can be used in the function signature and body.
|
||||||
|
// The constraint comparable means that the type E must support the == operator, which is necessary for comparing the value with the elements of the slice.
|
||||||
func SlicesIndex[S []E, E comparable](s S, v E) int {
|
func SlicesIndex[S []E, E comparable](s S, v E) int {
|
||||||
for i := range s {
|
for i := range s {
|
||||||
if v == s[i] {
|
if v == s[i] {
|
||||||
|
|
@ -11,15 +14,21 @@ func SlicesIndex[S []E, E comparable](s S, v E) int {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// represents a linked list of any type T. The type parameter T is used in the definition of List and its methods,
|
||||||
|
// and it can be instantiated with any type when we create a List value. The type any is in fact defined as
|
||||||
|
// an empty interface. Thanks to duck-typing it can be used to represent any type, but it does not provide any
|
||||||
|
// operations on the values of that type.
|
||||||
type List[T any] struct {
|
type List[T any] struct {
|
||||||
head, tail *element[T]
|
head, tail *element[T]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the generic element of the linked list
|
||||||
type element[T any] struct {
|
type element[T any] struct {
|
||||||
next *element[T]
|
next *element[T]
|
||||||
val T
|
val T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adds an instance of the generic type to the list
|
||||||
func (lst *List[T]) Push(v T) {
|
func (lst *List[T]) Push(v T) {
|
||||||
if lst.tail == nil {
|
if lst.tail == nil {
|
||||||
lst.head = &element[T]{val: v}
|
lst.head = &element[T]{val: v}
|
||||||
|
|
@ -30,7 +39,8 @@ func (lst *List[T]) Push(v T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lst *List[T]) AllElements() []T {
|
// returns all elements of the list as a slice of the generic type.
|
||||||
|
func (lst List[T]) AllElements() []T {
|
||||||
var elems []T
|
var elems []T
|
||||||
for e := lst.head; e != nil; e = e.next {
|
for e := lst.head; e != nil; e = e.next {
|
||||||
elems = append(elems, e.val)
|
elems = append(elems, e.val)
|
||||||
|
|
@ -39,14 +49,17 @@ func (lst *List[T]) AllElements() []T {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var s = []string{"foo", "bar", "zoo"}
|
|
||||||
fmt.Println("index of zoo:", SlicesIndex(s, "zoo"))
|
|
||||||
|
|
||||||
_ = SlicesIndex[[]string, string](s, "zoo")
|
var s = []string{"foo", "bar", "zoo"}
|
||||||
|
// The types S and E are explicitly specified as []string and string respectively.
|
||||||
|
fmt.Println("index of zoo:", SlicesIndex[[]string, string](s, "zoo"))
|
||||||
|
|
||||||
var s2 = []int{2, 4, 5, 6}
|
var s2 = []int{2, 4, 5, 6}
|
||||||
|
// The type E is inferred from the type of the value 4, which is int,
|
||||||
|
// and the type S is inferred from the type of the slice s2, which is []int.
|
||||||
fmt.Println("index of 4: ", SlicesIndex(s2, 4))
|
fmt.Println("index of 4: ", SlicesIndex(s2, 4))
|
||||||
|
|
||||||
|
// instantiate a list of float64 elements
|
||||||
lst := List[float64]{}
|
lst := List[float64]{}
|
||||||
lst.Push(10)
|
lst.Push(10)
|
||||||
lst.Push(13)
|
lst.Push(13)
|
||||||
|
|
|
||||||
|
|
@ -7,18 +7,21 @@ import (
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
f := createFile("/tmp/defer.txt")
|
|
||||||
defer closeFile(f)
|
|
||||||
writeFile(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createFile(p string) *os.File {
|
|
||||||
fmt.Println("creating")
|
fmt.Println("creating")
|
||||||
f, err := os.Create(p)
|
f, err := os.Create("/tmp/defer.txt")
|
||||||
|
|
||||||
|
// defer the closing of the file until the surrounding function returns.
|
||||||
|
// This ensures that the file will be closed even if there is a panic or an early return in the function.
|
||||||
|
defer closeFile(f)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return f
|
|
||||||
|
//will not be called in case of a panic before
|
||||||
|
writeFile(f)
|
||||||
|
|
||||||
|
// closeFile will be called here due to the defer-statement, even in case of a panic before.
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeFile(f *os.File) {
|
func writeFile(f *os.File) {
|
||||||
|
|
@ -28,8 +31,10 @@ func writeFile(f *os.File) {
|
||||||
|
|
||||||
func closeFile(f *os.File) {
|
func closeFile(f *os.File) {
|
||||||
fmt.Println("closing")
|
fmt.Println("closing")
|
||||||
err := f.Close()
|
var err error
|
||||||
|
if f != nil {
|
||||||
|
err = f.Close()
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue