За какво често се полва sync.WaitGroup?
За какво бихте използвали sync.RWMutex?
sync.Mutex при ситуации, в които част от горутините само четатКакво ще направи select ако никой не пише в каналите му в даден момент?
default код, ще го изпълниКакво би направил следния код:
package main
import "fmt"
import "time"
func main() { c := make(chan int) go func() { c <- 42 }() select { case v1 := <-c: fmt.Printf("received %d in v1\n", v1) case v2 := <-c: fmt.Printf("received %d in v2\n", v2) case <-time.After(1 * time.Nanosecond): fmt.Printf("timeout\n") default: fmt.Printf("nothing!\n") } }
Who knows :)
Как се правят generators/iterators/lazy loading в Go?
error handlingerrno#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
}defer е специален механизъм на езикаdefer добавя извикване на функция в един списък (стек)mutex-и, etc.)func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
dst, err := os.Create(dstName)
if err != nil {
return
}
written, err = io.Copy(dst, src)
dst.Close()
src.Close()
return
}Какви са проблемите с този код?
func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()
dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close()
return io.Copy(dst, src)
}defer statement-ите ни позволяват да мислим за затварянето на файловете веднага след отварянето имdefer се оценяват, когато самият defer statement се оценяваfunc a() {
i := 0
defer fmt.Println(i)
i++
return
}LIFO редfunc b() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
}defer -натите функции могат да "пипат" по именованите връщани аргументи на обграждащата функцияfunc c() (i int) {
defer func() { i++ }()
return 1
}package main
import (
"fmt"
)
func deferExample() { for i := 0; i < 5; i++ { defer func(i int) { fmt.Printf(" %v", i) }(i) } }
func main() {
deferExample()
}
-
package main
import (
"fmt"
)
func deferExample() { for i := 0; i < 5; i++ { defer func() { fmt.Printf(" %v", i) }() } }
func main() {
deferExample()
}
panic е вградена функцияpanic, изпълнението на F спира, всички `defer`-нати функции на F се изпълняват нормално, след което изпълнението се връща във функцията, извикала Fpanicthread) не свършат, когато програмата гърмиpanic, както и след разни runtime грешки, като out-of-bounds array access
recover е безполезен без defer ( може да се съвземете само в defer )recover не прави нищо (връща nil), ако текущата горутина не е в паникаrecover връща аргумента, подаден на panicfunc 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
Днес няма да си говорим за acceptance testing, quality assurance или нещо, което се прави от "по-низшия" отдел във фирмата.
Всичко тук е дело на програмиста.
Проектът идва с готово, подробно задание.
Прави се дизайн.
С него работата се разбива на малки задачи.
Те се извършват последователно.
За всяка от тях пишете кода и приключвате.
Изискванията не се променят, нито се добавя нова функционалност.
Щом съм написал един код, значи ми остава единствено да го разцъкам - няколко print-а, малко пробване в main функцията и толкова.
Така или иначе няма да се променя.
А ако (не дай си боже) това се случи - аз съм го писал, знам го, няма как да допусна грешка.
Най-много да го поразцъкам още малко.
Заданията винаги се променят.
Често се налага един код да се преработва.
Писането на код е сложна задача - допускат се грешки.
Програмистите са хора - допускат грешки.
Промяната на модул в единия край на системата като нищо може да счупи модул в другия край на системата.
Идва по-добра идея за реализация на кода, по ред причини.
За всичко съмнително ще пишем сценарий, който да "цъка".
Всеки сценарий ще изпълнява кода и ще прави няколко твърдения за резултатите.
Сценариите ще бъдат обединени в групи.
Пускате всички тестове с едно бутонче.
Резултатът е "Всичко мина успешно" или "Твърдения X, Y и Z в сценарии A, B и C се оказаха неверни".
Искаме да тестваме и производителността на нашия код.
n пъти и записват времето, отнело за изпълнениеРазбрахме се, че тестовете са ни супер важни.
Очевидно в стандартната библиотека на Go, има пакет за това.
За да тестваме foo.go, създаваме foo_test.go в същата директория, който тества foo.go
Ако тестваме пакета foo можем:
foo.foo_test.Или да ги смесваме.
testing.TTest и слеващата буква да е главна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.BBenchmark и слеващата буква да е главна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
Example, последвана от името на типа или функциятаFoo -> ExampleFoo
Bar го слагаме с подчертавка след типа ExampleFoo_BarOutput: и ще бъде тествано че изхода на кода съвпада с останалата част от коментараfunc ExampleHello() { Hello("hello") // Output: // hello }