Какво прави select
?
error handling
errno
#include <stdio.h> #include <errno.h> #include <string.h> extern int errno; int main () { FILE* pf = fopen("unexist.txt", "rb"); if (pf == NULL) { fprintf(stderr, "Value of errno: %d\n", errno); perror("Error printed by perror"); fprintf(stderr, "Error opening file: %s\n", strerror(errno)); } else { fclose(pf); } return 0; }
Има грубо-казано 2 начина
type error interface { Error() string }
os.Open
връща os.PathError
:type PathError struct { Op string // "open", "unlink", etc. Path string // Файлът с грешката Err error // Грешката, върната от system call-a } func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
func ReadFile(filename string) ([]byte, error) { f, err := os.Open(filename) if err != nil { return nil, err } //... }
или малко по-сложно:
func CreateFile(filename string) (*os.File, error) { var file, err = os.Create(filename) if e, ok := err.(*os.PathError); ok && e.Err == syscall.ENOSPC { deleteTempFiles() // Free some space return os.Create(filename) } return file, err }
Често оплакване на Go програмисти е количеството проверки за грешки:
if err != nil { return err }
os
, io
, net
, etc.)if _, err := fd.Write(p0[a:b]); err != nil { return err } if _, err := fd.Write(p1[c:d]); err != nil { return err } if _, err := fd.Write(p2[e:f]); err != nil { return err }
Може да стане:
var err error write := func(buf []byte) { if err == nil { _, err = w.Write(buf) } } write(p0[a:b]) write(p1[c:d]) write(p2[e:f]) if err != nil { return err }
error
интерфейсаNew(string)
от пакета errors
:func someFunc(a int) (someResult, error) { if a <= 0 { return nil, errors.New("a must be positive!") } // ... }
fmt.Errorf
:func someFunc(a int) (someResult, error) { if a <= 0 { return nil, fmt.Errorf("a is %d, but it must be positive!", a) } // ... }
defer
е специален механизъм на езикаdefer
добавя извикване на функция в един списък (стек)package main
import "fmt"
func main() { fmt.Println("start") defer fmt.Println("first") if false { // ex. try to open a file fmt.Println("error") return } defer fmt.Println("second") fmt.Println("done") }
panic
е вградена функцияpanic
, изпълнението на F спира, всички defer
-нати функции на F се изпълняват нормално, след което изпълнението се връща във функцията, извикала Fpanic
thread
) не свършат, когато програмата гърмиpanic
, както и след разни runtime грешки, като out-of-bounds array access
recover
е безполезен без defer
( може да се съвземете само в defer )recover
не прави нищо (връща nil
), ако текущата горутина не е в паникаrecover
връща аргумента, подаден на panic
func f() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered in f", r) } }() fmt.Println("Calling g.") g(0) fmt.Println("Returned normally from g.") } func g(i int) { if i > 2 { fmt.Println("Panicking!") panic(fmt.Sprintf("%v", i)) } defer fmt.Println("Defer in g", i) fmt.Println("Printing in g", i) g(i + 1) }
package main
import "fmt"
func main() { f() fmt.Println("Returned normally from f.") }
func f() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
fmt.Println("Calling g.")
g(0)
fmt.Println("Returned normally from g.")
}
func g(i int) {
if i > 2 {
fmt.Println("Panicking!")
panic(fmt.Sprintf("%v", i))
}
defer fmt.Println("Defer in g", i)
fmt.Println("Printing in g", i)
g(i + 1)
}
//END OMIT
Go 2?!
До сега не сме ви говорили за Go2, така че настанете се удобно...
Има две интересни секции в черновата за Go2 предложение:
Днес няма да си говорим за acceptance testing, quality assurance или нещо, което се прави от QA отдела във фирмата.
Всичко тук е дело на програмиста.
Проектът идва с готово, подробно задание.
Прави се дизайн.
С него работата се разбива на малки задачи.
Те се извършват последователно.
За всяка от тях пишете кода и приключвате.
Изискванията не се променят, нито се добавя нова функционалност.
Щом съм написал един код, значи ми остава единствено да го разцъкам - няколко print-а, малко пробване в main функцията и толкова.
Така или иначе няма да се променя.
А ако (не дай си боже) това се случи - аз съм го писал, знам го, няма как да допусна грешка.
Най-много да го поразцъкам още малко.
Заданията винаги се променят.
Често се налага един код да се преработва.
Писането на код е сложна задача - допускат се грешки.
Програмистите са хора - допускат грешки.
Промяната на модул в единия край на системата като нищо може да счупи модул в другия край на системата.
Идва по-добра идея за реализация на кода, по ред причини.
За всичко съмнително ще пишем сценарий, който да "цъка".
Всеки сценарий ще изпълнява кода и ще прави няколко твърдения за резултатите.
Сценариите ще бъдат обединени в групи.
Пускате всички тестове с едно бутонче.
Резултатът е "Всичко мина успешно" или "Твърдения X, Y и Z в сценарии A, B и C се оказаха неверни".
Искаме да тестваме и производителността на нашия код.
n
пъти и записват времето, отнело за изпълнениеРазбрахме се, че тестовете са ни супер важни.
Очевидно в стандартната библиотека на Go, има пакет за това.
За да тестваме foo.go
, създаваме foo_test.go
в същата директория, който тества foo.go
Ако тестваме пакета foo
можем:
foo
foo_test
С go test ./...
пускаме тестовете на един цял проект :)
testing.T
Test
и слеващата буква да е главнаt.Error[f]?()
, t.Fail()
, t.Fatal()
...func TestFibonacciFastest(t *testing.T) { n := FibonacciFastest(0) if n != 1 { t.Error("FibonnaciFastest(0) returned" + n + ", we expected 1") } }
t.Skip()
t.Parallel()
t.Parallel()
testing.B
Benchmark
и слеващата буква да е главнаfor
цикъл, извикващ b.N
пъти тестваната функцияgo
е достатъчно умен да реши колко пъти да я извика, за да получи адекватни резултатиgithub.com/ChristianSiegert/go-testing-example
func BenchmarkFibonacciFastest(b *testing.B) { for i := 0; i < b.N; i++ { FibonacciFastest(40) } }
go
генерира автоматична документация на нашия код, вземайки под внимание:
/* Example fobonacci numbers implementation */ package fibonacci
// Fastest Fibonacci Implementation func FibonacciFastest(n uint64) uint64 {
// lookupTable stores computed results from FibonacciFast or FibonacciFastest. var lookupTable = map[uint64]uint64{}
На всички локално инсталирани пакети
godoc -http=:6060
Документация на (почти) всички go пакети качени в BitBucket, GitHub, Launchpad и Google Project Hosting
func testingFunc(tb testing.TB, input, expectedResult int) { if result := Fibonacci(input); result != expectedResult { tb.Fatalf("Expected %d for Fiboncci(%d) but got %d", expectedResult, input, result) } }
func TestFibonacci(t *testing.T) { var tests = []struct { input, result int }{ {input: 2, result: 1}, {input: 8, result: 21}, {input: 10, result: 55}, } for _, test := range tests { testingFunc(t, test.input, test.result) } }
func TestSubFibonacci(t *testing.T) { var tests = []struct { input, result int }{ {input: 2, result: 1}, {input: 8, result: 21}, {input: 10, result: 55}, } for _, test := range tests { t.Run(fmt.Sprintf("Fibonacci(%d)", test.input), func(t *testing.T) { testingFunc(t, test.input, test.result) }) } }
--- FAIL: TestFibonacci (0.00s) table_test.go:68: Expected 1 for Fiboncci(2) but got 21 --- FAIL: TestSubFibonacci (0.00s) --- FAIL: TestSubFibonacci/Fibonacci(2) (0.00s) table_test.go:68: Expected 1 for Fiboncci(2) but got 21 --- FAIL: TestSubFibonacci/Fibonacci(10) (0.00s) table_test.go:68: Expected 55 for Fiboncci(10) but got 21 FAIL
func BenchmarkSubFibonacci(b *testing.B) { var tests = []struct { input, result int }{ {input: 2, result: 1}, {input: 8, result: 21}, {input: 10, result: 55}, } for _, test := range tests { b.Run(fmt.Sprintf("BFibonacci(%d)", test.input), func(b *testing.B) { for i := 0; i < b.N; i++ { testingFunc(b, test.input, test.result) } }) } }
--- FAIL: BenchmarkSubFibonacci/BFibonacci(2) table_test.go:68: Expected 1 for Fiboncci(2) but got 21 BenchmarkSubFibonacci/BFibonacci(8)-16 1000000000 2.65 ns/op --- FAIL: BenchmarkSubFibonacci/BFibonacci(10) table_test.go:68: Expected 55 for Fiboncci(10) but got 21 --- FAIL: BenchmarkSubFibonacci FAIL
func TestGroupSubFibonacci(t *testing.T) { t.Run("group1", func(t *testing.T) { for _, test := range tests { t.Run(fmt.Sprintf("Fibonacci(%d)", test.input), func(t *testing.T) { var input, result = test.input, test.result t.Parallel() time.Sleep(time.Second) testingFunc(t, input, result) }) } t.Run("NonParallel", func(t *testing.T) { t.Fatal("Just cause") }) t.Fatal("Oops") t.Run("NonParallel2", func(t *testing.T) { t.Fatal("Just Cause II") }) }) }
--- FAIL: TestGroupSubFibonacci (1.00s) --- FAIL: TestGroupSubFibonacci/group1 (0.00s) --- FAIL: TestGroupSubFibonacci/group1/NonParallel (0.00s) table_test.go:96: Just cause table_test.go:98: Oops --- FAIL: TestGroupSubFibonacci/group1/Fibonacci(2) (1.00s) table_test.go:69: Expected 1 for Fiboncci(2) but got 21 --- FAIL: TestGroupSubFibonacci/group1/Fibonacci(10) (1.00s) table_test.go:69: Expected 55 for Fiboncci(10) but got 21 FAIL
Example
, последвана от името на типа или функциятаFoo -> ExampleFoo
Bar
го слагаме с подчертавка след типа ExampleFoo_Bar
Output:
и ще бъде тествано че изхода на кода съвпада с останалата част от коментараfunc ExampleHello() { Hello("hello") // Output: // hello }
type Sleeper interface { Sleep(time.Duration) }
mock
пакет