├── .gitignore ├── 00-course-info.slide ├── 01-intro.slide ├── 02-funcs_and_pointers.slide ├── 03-data_structures.slide ├── 04-types_and_interfaces.slide ├── 05-concurrency101.slide ├── 06-concurrency102.slide ├── 07-errors_and_testing.slide ├── 08-homework-retrospective.slide ├── 09-tools.slide ├── 10-cgo-build.slide ├── 11-dependency-management.slide ├── 12-faq-and-pprof.slide ├── LICENSE ├── Makefile ├── README.md ├── assets ├── armed.jpg ├── code-coverage-html.png ├── commandline.png ├── creators.jpg ├── exorcist.jpg ├── fanin.jpg ├── go-lectures-summary-qr.png ├── go-tool-all-the-things.jpg ├── golang-only.png ├── gopher-02-door-communication.jpg ├── gopher-02-door.jpg ├── gopher-cute.jpg ├── gopher.jpg ├── gopherbw.png ├── gophereartrumpet.jpg ├── gopherswrench.jpg ├── hamilton-code.jpg ├── hoare.jpg ├── interface.png ├── interface0.png ├── java-vs-cpp-vs-golang.png ├── java-vs-golang.png ├── panic.jpg ├── slice.jpg ├── stack-overflow-go-c-cpp-java.png ├── stack-overflow-go-only.png ├── static │ ├── article.css │ ├── dir.css │ ├── dir.js │ ├── favicon.ico │ ├── jquery-ui.js │ ├── jquery.js │ ├── play.js │ ├── playground.js │ ├── print.css │ ├── slides.js │ └── styles.css └── templates │ ├── action.tmpl │ ├── article.tmpl │ ├── dir.tmpl │ └── slides.tmpl ├── code ├── cgo-runtime │ ├── Makefile │ ├── bar.c │ ├── bar.s │ ├── cgo.go │ ├── foo.go │ ├── foo.o │ ├── foo.s │ ├── func_point.go │ ├── gcc_foo.s │ ├── goc │ │ ├── goc.c │ │ ├── goc.go │ │ └── goc.h │ ├── math │ │ ├── math.go │ │ └── math_test.go │ ├── unsafe.go │ └── unsafe_2.go ├── concurrency101 │ ├── boring-with-sync.go │ ├── boring.go │ ├── c_fork.c │ ├── c_fork_sync.c │ ├── c_threads.c │ ├── channel-simple-demo.go │ ├── closing-channels.go │ ├── deadlock.go │ ├── generator.go │ ├── go-less-boring-sleep.go │ ├── go-less-boring.go │ ├── less-boring.go │ ├── map-keys.go │ ├── python_threads.py │ └── synchronization.go ├── concurrency102 │ ├── boring-with-sync.go │ ├── closed.go │ ├── closed_range.go │ ├── closing-channels-range.go │ ├── cond.go │ ├── daisy-chain.go │ ├── deadlock_detection.go │ ├── dummy_fanin.go │ ├── error_demo.go │ ├── fib.go │ ├── go-less-boring-sleep.go │ ├── indeterminate-select.go │ ├── muffin-answer-func-chan-methods.go │ ├── mutex.go │ ├── mutex_chan.go │ ├── once.go │ ├── pipes.c │ ├── select_fanin.go │ ├── select_finish_fanin.go │ ├── timeout.go │ └── waitgroup.go ├── course-info │ ├── hello_http.go │ └── hello_world.go ├── data_structures │ ├── array.go │ ├── array_comparison.go │ ├── array_comparison_error.go │ ├── len_vs_cap.go │ ├── new_vs_make.go │ └── slices_copy_examples.go ├── errors_and_testing │ ├── c_err_example.c │ ├── defer_example.go │ ├── muffin-q3.go │ ├── panic.go │ ├── table_test.go │ └── testing.go ├── funcs_and_pointers │ ├── as_values.go │ ├── broken_pointer.go │ ├── defer_example1.go │ ├── defer_example2.go │ ├── defer_example_0.go │ ├── factorial.go │ ├── lambdas.go │ ├── lots_of_arguments.go │ ├── many_results.go │ ├── named_sum_and_count.go │ ├── pointers.go │ ├── simple_funcs.go │ └── visibility.go ├── goruntime │ ├── easter_eggs.go │ └── esc.go ├── intro │ ├── endless_for.go │ ├── hello_world.go │ ├── hello_world_with_useless_import.go │ ├── implicit_cast_error.go │ ├── init.go │ ├── iota.go │ ├── strings_and_runes.go │ ├── unused_var.go │ └── wrong_type_inference.go ├── types_and_interfaces │ ├── anon_structs.go │ ├── bound_methods.go │ ├── custom_json.go │ ├── integer.go │ ├── interface_conversions.go │ ├── nil_objects.go │ ├── plain_json.go │ ├── shapes.go │ ├── stringer.go │ ├── type_aliases.go │ └── type_assertion.go └── web-programming │ ├── file-server.go │ ├── handlers-mux.go │ ├── http-client.go │ └── http-server.go └── index.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /00-course-info.slide: -------------------------------------------------------------------------------- 1 | Програмиране с Go 2 | 03.10.2018 3 | 4 | fmi@golang.bg 5 | http://fmi.golang.bg/ 6 | @fmi_golang 7 | 8 | * Здравейте 9 | 10 | Нека ви обясним къде сте попаднали. 11 | 12 | * За пета (не съвсем поредна) година 13 | 14 | - Много оценяваме вашия feedback и се опитваме всяка година да сме по-добри 15 | - Вече има резултат... 16 | - ... разбираме от корейски интерфейси на проектори! 17 | - Курсът ще е доста труден 18 | 19 | * Лекции 20 | 21 | - Сряда 22 | - От 19:15 до около 21:00 23 | - Зала 02 във ФМИ 24 | 25 | * В интернеда 26 | 27 | .link http://fmi.golang.bg/ 28 | 29 | .link https://github.com/fmi/go-lectures 30 | 31 | .link https://github.com/fmi/go-homework 32 | 33 | .link https://www.facebook.com/groups/fmi.golang/ 34 | 35 | .link http://twitter.com/fmi_golang @fmi_golang 36 | 37 | 38 | * Торонто 🍕🏚 39 | 40 | Вече не съществува 41 | 42 | Сърцата ни са разбити 43 | 44 | Но ще намерим къде да пием бира след курса. Добре дошли! 45 | 46 | 47 | * Оценяване 48 | 49 | Ще събирате точки 50 | 51 | 52 | * Домашни 53 | 54 | - 5-6 броя 55 | - Всяко дава по 10 точки 56 | - Ако много ни радават решения, можем да даваме бонуси 57 | - Ако вършите глупости, вземаме точки 58 | - Предават се онлайн на сайта на курса 59 | - Проверяват се автоматизирано 60 | - Стават публичния след крайния срок 61 | - Ако преписвате, ви късаме 62 | 63 | 64 | * Тестове 65 | 66 | - Два броя 67 | - 30 въпроса 68 | - 1 верен отговор = 1 точка 69 | 70 | 71 | * Проект 72 | 73 | - Трябва сами да си харесате тема 74 | - Ще ви дадем примерни 75 | - До Бъдни вечер трябва да сте ни изпратили своята идея и ние да сме я одобрили 76 | - В някоя от първите учебни седмици на 2019 ще защитавате структурата на проекта 77 | - По време на сесията защитавате имплементация, тестове, стил... 78 | - Само миналите първата защита отиват на изпит 79 | - Oстаналите - септември 80 | 81 | 82 | * Но поне сме щедри с точките 83 | 84 | 85 | * Участие по време на лекция 86 | 87 | - Ще раздаваме мъфини или Snickers-и 88 | - Всеки мъфин/Snickers има код 89 | - Всеки код носи точка 90 | - Даваме ги при добър въпрос или отговор на въпрос 91 | - Ще даваме мъфин и ако ни хванете в грешка 92 | 93 | 94 | * Участие в сайта 95 | 96 | - Добър пост във форумите (въпрос, отговор на въпрос или помощ на колега) 97 | - Добър коментар на чуждо решение 98 | - Предложена хитринка 99 | 100 | 101 | * Помощ с материалите 102 | 103 | - Качваме всяка лекция в [[https://github.com/fmi/go-lectures][GitHub]] 104 | - Поправяне на грешки 105 | - Примери от лекциите 106 | - Неща, които сме пропуснали 107 | 108 | 109 | * Предизвикателства 110 | 111 | - Малки или странни задачки 112 | - Неособено дълъг срок 113 | - Обикновено са изненада дори за нас 114 | 115 | 116 | * Защо всичко трябва да бъде толкова сложно!? 117 | 118 | 119 | * Go 120 | 121 | * Що е то Go? 122 | 123 | - Сравнително нов програмен език 124 | - Започнат 2007 година в Google от Robert Griesemer, Rob Pike и Ken Thompson 125 | - Първата публична версия е от 2009, а версия 1.0 излиза през 2012 126 | - Последния major release е 1.11 от преди месец 127 | - За контраст - C е на 46 години, Python на 27 и Java на 23 128 | - Хардуерът се променя 129 | - Проблемите, които решаваме, също 130 | 131 | * ... 132 | 133 | - Go е език с отворен код от сравнително ниско ниво (между C/C++ и Java/.NET) 134 | - Има C-подобен синтаксис 135 | - Дълбоко залегнала идея за конкурентност 136 | - Вграден, бърз и ефективен garbage collector 137 | - Безумно бърза компилация до машинен код 138 | - Минимален runtime, single statically-linked executable 139 | - Cross-platform: Linux, OS X, Windows, BSD-та и Unix-и, mainframes, WebAssembly и мобилни устройства 140 | - Създаден да scale-ва 141 | - Изключително опростен 142 | - UTF-8 by default 143 | 144 | * ... 145 | 146 | - Safe by default, но `unsafe` при необходимост 147 | - Богата стандартна библиотека 148 | - Много динамично развиващ се език 149 | - Вградени инструменти за unit и benchmark тестове 150 | - Race condition detector 151 | - Документация като feature 152 | - Екосистема от удобни инструменти и сайтове 153 | - Единна конвенция за писане на код 154 | - Интеграция със C (макар и леко гадна) 155 | - `go`get`-u`golang.org/x/tools/cmd/present` 156 | 157 | * ... и има ли почва у нас? 158 | 159 | - Docker, rkt, etcd, fleet, deis, consul, terraform, kubernetes и сума ти контейнерни библиотеки и приложения 160 | - tidb, CockroachDB, InfluxDB, BoltDB, vitess 161 | - Grafana, ngrok, Gogs и много [[https://github.com/avelino/awesome-go][други проекти]] 162 | - От доста време се използва в production от Google, Cloudflare, Soundcloud и [[https://github.com/golang/go/wiki/GoUsers][доста други компании]] 163 | 164 | * hello world 165 | 166 | .play code/course-info/hello_world.go 167 | 168 | * hello world on steroids 169 | 170 | .play code/course-info/hello_http.go /func main/,/^}/ 171 | 172 | * The Go gopher 173 | 174 | .image assets/gopherbw.png 175 | 176 | * Подробности 177 | 178 | Вид гризач 179 | 180 | Създаден от [[https://www.instagram.com/reneefrench][Renée French]] 181 | 182 | [[https://blog.golang.org/gopher][История]] 183 | 184 | И отговорът на въпроса, който всички си задавате... 185 | 186 | _да_, можете да го купите онлайн. 187 | 188 | -------------------------------------------------------------------------------- /02-funcs_and_pointers.slide: -------------------------------------------------------------------------------- 1 | Функции и указатели 2 | 17.10.2018 3 | 4 | http://play.golang.org/ 5 | 6 | fmi@golang.bg 7 | http://fmi.golang.bg/ 8 | @fmi_golang 9 | 10 | * Но преди това... 11 | 12 | 13 | * Въпрос за мъфин #1 14 | 15 | Възможно ли е да използвам go1.11 за да компилирам програма от 2012г.? Защо? 16 | 17 | - Да 18 | - [[https://golang.org/doc/go1compat][go1compat гаранция]] 19 | 20 | * Въпрос за мъфин #2 21 | 22 | Какъв тип връша main функцията? 23 | 24 | - Не връща нищо 25 | 26 | 27 | * Въпрос за мъфин #3 28 | 29 | Можем ли да дефинираме променлива без да пишем типа излишно? 30 | 31 | Да - чрез [[https://tour.golang.org/basics/14][type inference]] 32 | 33 | a := 42 34 | b := "a string" 35 | 36 | Не можем да го ползваме в глобалния скоуп 37 | 38 | * Въпрос за мъфин #4 39 | 40 | Как се import-ва пакет, публикуван публично в gitlab? 41 | 42 | - Използва се URL-a му 43 | 44 | import "gitlab.com/user/awesomepack" 45 | 46 | * Въпрос за мъфин #5 47 | 48 | Какъв е идиоматичния начин за правене на enum в Go? 49 | 50 | За какво се използва `iota` в Go и коя подред буква в гръцката азбука е? 51 | 52 | - `iota` 53 | 54 | const ( 55 | City = iota 56 | Town 57 | Village 58 | ) 59 | 60 | * Здравна сигурност 61 | 62 | Мъфините съдържат 63 | 64 | - Мляко, яйца, лешници, какао, шоколад, захар, брашно, слънчогледово олио 65 | - Абе, всичко към което е възможно да сте алергични 66 | - Моля, не се наранявайте с тях! 67 | 68 | * ... и няколко неща, които пропуснахме 69 | 70 | 71 | * Това е гофер (gopher) 72 | 73 | .image assets/gopherbw.png 74 | 75 | 76 | * Те са сладки 77 | 78 | .image assets/gopher-cute.jpg 79 | .link http://smallfarms.oregonstate.edu 80 | 81 | 82 | * Отворете сърцето си за gopher-ите 83 | 84 | .image assets/gopherswrench.jpg 85 | 86 | note: Не сме секта. 87 | 88 | * Credits 89 | 90 | Сладкият Gopher е създаден от [[http://www.reneefrench.com][Renée French]]. Той е централна причина за успеха на езика. 91 | 92 | * Funcs 101 93 | 94 | - Функциите са основна структурна единица в Go 95 | - Всеки проблем, който някога ще решавате, ще е разбит на функции 96 | - Една функция върши точно едно нещо 97 | - ... в противен случай правите нещо грешно 98 | - Може да връща няколко резултата 99 | - Последните три точки не си противоречат взаимно 100 | - DRY 101 | - Редът, в който са дефинирани, не е от значение 102 | - Може и да изпада в паника (буквално) 103 | 104 | 105 | * С ключовата дума func могат да се създават три неща 106 | 107 | 1. Функции 108 | 109 | 2. Ламбди 110 | 111 | 3. Методи 112 | 113 | 114 | * Функции 115 | 116 | .play code/funcs_and_pointers/simple_funcs.go 117 | 118 | * Аргументи 119 | 120 | func foo(a int, b string) float64 121 | 122 | Функцията foo приема int и string и връща float64 123 | 124 | - Винаги работим върху копия от аргументите 125 | - Ако това не ни устройва, подаваме указател (за тях след малко) 126 | - Нямаме стойности по подразбиране (optional parameters) или overloading 127 | 128 | Когато няколко аргумента са от един тип: 129 | 130 | func bar(a, b int, c float64) float64 131 | 132 | * Произволен брой аргументи 133 | 134 | func sum(args ...int) int 135 | 136 | Функцията sum приема произволен брой числа и връща техния сбор 137 | 138 | .code code/funcs_and_pointers/lots_of_arguments.go /^func sum/,/^}/ 139 | 140 | Извикваме я с колкото ни трябват 141 | 142 | sum() //0 143 | sum(2, 3) //5 144 | sum(2, 3, 4, 5) //14 145 | 146 | Трябва да е последния аргумент на функцията 147 | * Следващия път ще говорим по-подробно за `range` 148 | 149 | * Множество стойности като резултат 150 | 151 | .play code/funcs_and_pointers/many_results.go 152 | 153 | * Защо? 154 | 155 | - Няма нуждата от някои грозни C идиоми, като ... 156 | - ... модифициране на аргумент, подаден по адрес 157 | - ... errno 158 | - По-лесно справяне с грешки 159 | 160 | 161 | * Как е реализирано в "стари" езици: 162 | 163 | - C/C++ и компания: масив или структура 164 | - Python: tuple-и 165 | 166 | * Как е реализирано в Go? 167 | - Много просто: връщате каквото ви трябва 168 | 169 | * Ами ако не ни трябват всичките резултати? 170 | 171 | 1. Знаем, че ако дефинираме променлива и не я използваме, гърми 172 | 2. Ако искаме онзи сбор и не ни интересува броят аргументи, това ще изгърми 173 | 174 | result, count := sumAndCount(2, 3, 4, 5) 175 | 176 | 3. Ако нямаме нужда от дадена стойност, я присвояваме на _: 177 | 178 | result, _ := sumAndCount(2, 3, 4, 5) 179 | 180 | - Тя не се запазва и не можем да я достъпим след това 181 | - По-полезно е, отколкото ви се струва 182 | 183 | 184 | * Именовани резултати 185 | 186 | .code code/funcs_and_pointers/named_sum_and_count.go /^func sum/,/^}/ 187 | 188 | - Резултатите се инициализират преди изпълнението на функцията 189 | - Добра идея са, ако пишем често в тях по време на изпълнение 190 | - Няма нужда да ги указваме при return 191 | 192 | * Фунцкиите като стойности 193 | 194 | - В реда на нещата е функция да приема функция 195 | 196 | .code code/funcs_and_pointers/as_values.go /^func foo/,/^}/ 197 | 198 | - Няма нищо лошо в това и да връщаме функция 199 | 200 | .code code/funcs_and_pointers/as_values.go /^func createRandomGenerator/,/^}/ 201 | 202 | - Въобще, можем да ги присвояваме на стойност 203 | - Но можем да ги сравняваме само с `nil` 204 | - Дори може да върне себе си 205 | 206 | * ...или да изпълни себе си 207 | .play code/funcs_and_pointers/factorial.go /^func factorial/,/^}/ 208 | 209 | 210 | * Именоване на функции 211 | 212 | - Кратки, описателни имена 213 | - Започва с буква, последвана от букви, цифри или _ 214 | - Помните ли, че тук всеки стринг е UTF-8? 215 | - `ЗнаетеЛиЧеHelloWorldНаКитайскиЕ世界` е валидно име на функция 216 | - *НЕ* пишете такива имена! 217 | - camelCase 218 | - Ако функцията ни трябва извън текущия пакет: CamelCase 219 | 220 | 221 | * Анонимни функции 222 | 223 | func(x, y int) int { return x * y } 224 | 225 | - НЕ можем да дефинираме нормална функция в тялото на друга. 226 | - Но пък можем да създаваме ламбди 227 | - Ламбдите си нямат име... очевидно 228 | - Удобни са за дребни неща 229 | 230 | .play code/funcs_and_pointers/lambdas.go /^func main/,/^}/ 231 | 232 | * Scope, visibility & escape analysis 233 | 234 | .play code/funcs_and_pointers/visibility.go 235 | 236 | Променливата `count` нужна ли е въобще? 237 | 238 | * defer 239 | 240 | - `defer` е специален механизъм на езика 241 | - `defer` добавя *извикване* на функция в един списък (стек) 242 | - Когато обграждащата функция приключи, тези извиквания се изпълняват в обратен ред 243 | 244 | .play code/funcs_and_pointers/defer_example_0.go /^func main/,/^}/ 245 | 246 | * Пример: 247 | 248 | func CopyFile(dstName, srcName string) (written int64, err error) { 249 | src, err := os.Open(srcName) 250 | if err != nil { 251 | return 252 | } 253 | 254 | dst, err := os.Create(dstName) 255 | if err != nil { 256 | return 257 | } 258 | 259 | written, err = io.Copy(dst, src) 260 | dst.Close() 261 | src.Close() 262 | return 263 | } 264 | 265 | Какви са проблемите с този код? 266 | 267 | 268 | * По-красивият, правилен и работещ начин е това: 269 | 270 | func CopyFile(dstName, srcName string) (written int64, err error) { 271 | src, err := os.Open(srcName) 272 | if err != nil { 273 | return 274 | } 275 | defer src.Close() 276 | 277 | dst, err := os.Create(dstName) 278 | if err != nil { 279 | return 280 | } 281 | defer dst.Close() 282 | 283 | return io.Copy(dst, src) 284 | } 285 | 286 | `defer` се използва за сигурно и лесно почистване на ресурси (отворени файлове, заключени `mutex-и`, etc.) 287 | 288 | * Доуточнения 289 | 290 | - `defer` statement-ите ни позволяват да мислим за затварянето на файловете веднага след отварянето им 291 | - Това ни гарантира, че няма да забравим в никой случай за затварянето им, независимо кой, кога и как променя кода след нас 292 | 293 | * Три прости правила за defer (1) 294 | 295 | - Аргументите на `defer` се оценяват, когато самият `defer` statement се оценява 296 | 297 | func a() { 298 | i := 0 299 | defer fmt.Println(i) 300 | i++ 301 | return 302 | } 303 | 304 | - Това принтира "0" 305 | 306 | * Три прости правила за defer (2) 307 | 308 | - Функциите се изпълняват в `LIFO` ред 309 | 310 | func b() { 311 | for i := 0; i < 4; i++ { 312 | defer fmt.Print(i) 313 | } 314 | } 315 | 316 | - Това изписва "3210" 317 | 318 | * Три прости правила за defer (3) 319 | 320 | - `defer` -натите функции могат да "пипат" по именованите връщани аргументи на обграждащата функция 321 | 322 | func c() (i int) { 323 | defer func() { i++ }() 324 | return 1 325 | } 326 | 327 | - Тази функция връща "2" 328 | - Това е удобно, за да променяме връщаните стойности от функции, примерно за да върнем грешка 329 | 330 | * Примери 331 | 332 | .play code/funcs_and_pointers/defer_example1.go /^func deferExample/,/^}/ 333 | 334 | - 335 | 336 | .play code/funcs_and_pointers/defer_example2.go /^func deferExample/,/^}/ 337 | 338 | * Методи 339 | 340 | За тях ще си говорим като стигнем до дефиниране на типове 341 | 342 | 343 | * Указатели 344 | 345 | * Указатели 346 | .image assets/armed.jpg 347 | 348 | - Особен момент е, че нямаме аритметиката с указатели 349 | - Ако знаете как да ги ползвате в C/C++, нямате ядове 350 | 351 | * За останалите: Опреснителен курс 352 | 353 | - Всички променливи, константи и функции се пазят в оперативната памет 354 | - Всяка запазена стойност стои в отделна клетка 355 | - Всяка клетка си има уникален адрес (0xb6802, 0xfx04001d7f0) 356 | - За да вземем адреса на дадена променлива, използваме унарния оператор `&` 357 | - Имаме тип указател: `*int` 358 | - Указателите са с константна големина 359 | - Указател може да сочи към указател 360 | - В Go една стойност се изчиства от паметта, когато няма указатели към нея 361 | - Не можем да имаме указатели към константи 362 | - Очевидно `intP`==`*(&intP)` 363 | 364 | * Пример 365 | 366 | .play code/funcs_and_pointers/pointers.go 367 | 368 | * Как да си направим дупка в крака? 369 | 370 | .play code/funcs_and_pointers/broken_pointer.go 371 | 372 | * Указатели и функции 373 | 374 | func foo(a int, b *string) float64 375 | 376 | Функцията foo приема int и *указател* към string и връща float64 377 | 378 | Демек `a` бива копиран в скоупа на `foo`, а `b` просто сочи към някаква стойност отвън. 379 | 380 | - `b` не се копира. Ако в него има около 652183859 символа*, това е предимство 381 | - Каквото и да правим с `a` не влияе на нищо извън тази функция 382 | - Каквото и да направим с `b` променяме оригиналната стойност 383 | _*Следващия_път_ще_видим_защо_указател_към_string_може_да_не_ни_спести_кой-знае_колко_памет_ 384 | -------------------------------------------------------------------------------- /03-data_structures.slide: -------------------------------------------------------------------------------- 1 | Структури от данни 2 | 24.10.2018 3 | 4 | fmi@golang.bg 5 | http://fmi.golang.bg/ 6 | @fmi_golang 7 | 8 | * Но преди това... 9 | 10 | * Ретроспекция на домашното 11 | 12 | - функции за еднократна употреба 13 | - `defer` в `Generator` <3 14 | - left foldedness 15 | 16 | * Почти! Сега само малко... 17 | 18 | * Въпрос за мъфин #1 19 | 20 | Какво ще покаже следния код? 21 | 22 | func 4times4() (res int) { 23 | defer func() { 24 | res *= 4 25 | } 26 | return 4 27 | } 28 | fmt.Println(4times4()) 29 | 30 | - Синтактична грешка. `4times4` не е валидно име на функция. 31 | 32 | * Въпрос за мъфин #2 33 | 34 | С какво можем да сравняваме функции? 35 | 36 | - Само с `nil` 37 | 38 | * Въпрос за мъфин #3 39 | 40 | Ако знаем, че `foo` е указател към `int` в масив, то как можем да вземем следващия елемент в масива? 41 | 42 | - Не можем. Няма аритметика с указатели. 43 | 44 | * Въпрос за мъфин #4 45 | 46 | Кога се оценяват аргументите на `defer` функция? 47 | 48 | - По време на оценяване на `defer` израза. 49 | 50 | 51 | * Arrays 52 | 53 | - По нашенски: масиви 54 | - Последователност от еднакви по тип елементи 55 | - С конкретна фиксирана дължина 56 | 57 | .play code/data_structures/array.go /^func main()/,/^}/ 58 | 59 | Очевидно броим от 0 60 | 61 | 62 | * Инициализация 63 | 64 | var x [5]string 65 | x[0] = "Баба" 66 | x[1] = "меца" 67 | x[2] = "яде" 68 | x[3] = "от" 69 | x[4] = "медеца" 70 | 71 | или накратко: 72 | 73 | x := [6]float64{98, 93, 77, 82, 83} 74 | 75 | Чакай малко! Подали сме само 5 числа. 76 | 77 | x[5] == 0 78 | 79 | 80 | * Компилаторите могат да броят! 81 | 82 | - Да, и ние бяхме изненадани 83 | 84 | x := [...]string{"Incredible", "isn't", "it?"} 85 | 86 | - В този случай `x` е от тип `[3]string` 87 | 88 | * Сравняване на масиви 89 | 90 | Да разгледаме следния код 91 | 92 | .play code/data_structures/array_comparison.go /dqdo/,/fmt/ 93 | 94 | 95 | * Големината на масив е част от типа му 96 | 97 | .play code/data_structures/array_comparison_error.go /dqdo/,/fmt/ 98 | 99 | 100 | * Черва на масивите 101 | 102 | - Те са стойности, копират се, а не се предават по стойност 103 | 104 | func (foo [100]uint32) 105 | 106 | - Дори, когато правим 107 | 108 | a := [100]float64 109 | b := a 110 | 111 | - Може да се избегне с указател към масив 112 | 113 | func (foo *[100]uint32) 114 | 115 | - Няма нужда да го ползвате, ще видите защо след малко 116 | 117 | * Полезнотии 118 | 119 | - `len()` - връща размера като int 120 | - `range` - ключова дума, която позволява да итерираме по индекс и стойност 121 | 122 | for index, value := range arr { 123 | ... 124 | } 125 | 126 | 127 | 128 | for index := 0; index < len(arr); index++ { 129 | value := arr[index] 130 | ... 131 | } 132 | 133 | Тези два цикъла са еквивалентни 134 | 135 | _(Заради_range_нямаме_нужда_от_foreach)_ 136 | 137 | * Това е много готино, но... 138 | 139 | - Не могат да растат 140 | - Или да се свиват 141 | - Спомняте ли указателя към масив? А ако искаме да подадем масив с друга дължина? 142 | - Какво е това!? Живеем в 2018, не може ли да се направи нещо? 143 | 144 | 145 | * Slices 146 | 147 | Като масивите имат дължина и могат да се индексират, но дължината им може да се променя*. 148 | 149 | var x []float64 150 | 151 | Горното само създава променливата, а се инициализира по следния начин: 152 | 153 | x := make([]float64, 5) 154 | 155 | Това указва на слайса да бъде с размер 5. Всеки слайс е част от масив с не по-малка дължина от слайса. 156 | 157 | x := make([]float64, 5, 10) 158 | 159 | Това е същото като горното, но този слайс сочи към масив с размер 10. 160 | 161 | 162 | * Без изрично `make(...)` 163 | 164 | numbers := []float64{0, 1.2, 3.4, 55.3} 165 | 166 | - Създава slice-a "numbers" от тип []float64 и го запълва 167 | 168 | 169 | * Слайсове в действие 170 | 171 | arr := [6]float64{1, 2, 3, 4, 5, 6} 172 | x := arr[1:5] 173 | 174 | Създаваме слайс от втори до пети елемент включително на масива arr. 175 | 176 | x := arr[2:] // Взема всички без първите два елемента 177 | x := arr[:2] // Взема първите два елемента 178 | x := arr[:] // Взема всички елементи 179 | 180 | 181 | * Не точно масиви 182 | 183 | - Външно се държат като такива 184 | - Но всъщност един slice е (малка) структура, която е построена "върху" array 185 | - Всички операции с тях са евтини 186 | - Всяко оразмеряване прави _нов_ евтин слайс 187 | - ... който "сочи" към същия масив в паметта 188 | 189 | 190 | * Структура 191 | 192 | .image assets/slice.jpg 193 | 194 | x := []int{2, 3, 5, 7, 11} 195 | 196 | Създава нов slice, който сочи към нов масив от 5 елемента. 197 | 198 | y := x[1:3] 199 | 200 | Създава нов slice, но не и нов масив - използва се вече съществуващия за x. 201 | 202 | * Полезнотии 203 | 204 | 205 | * len и cap 206 | 207 | - `len(x)` - Взема размера на slice-а 208 | - `cap(x)` - Взема размера на масива, към който slice-а сочи 209 | 210 | .play code/data_structures/len_vs_cap.go /^func main()/,/^}/ 211 | 212 | * Нулева стойност 213 | 214 | - Както всичко в Go си имат нулева стойност и това е nil 215 | 216 | var foo []uint32 217 | foo == nil // True 218 | 219 | - `len` и `cap` връщат 0 за нулев slice 220 | 221 | len(foo) == cap(foo) == 0 222 | 223 | * Резултатни slice-ове 224 | 225 | - Ако разгледаме 226 | 227 | x := []uint32{0, 1, 2, 3, 4, 5, 6, 7} 228 | y := x[:] 229 | y[4] = 42 230 | x[4] == 42 // True 231 | 232 | - `y` не копира съдържанието на `x` 233 | - Kоето прави операцията много ефективна, независимо колко е голям `x` 234 | - Подаването като аргумент на функция копира самия slice, но не стойностите на масива, към който сочи 235 | - Създава се нов slice, който сочи към масива на оригинала 236 | 237 | * `cap` - втори епизод 238 | 239 | x := []uint32{0, 1, 2, 3, 4, 5, 6, 7} 240 | y := x[2:4] // [2, 3] 241 | y = y[:cap(y)] // [2, 3, 4, 5, 6, 7] 242 | 243 | - Опит да го увеличим над оригиналната големина на масива води до runtime panic 244 | 245 | * append 246 | 247 | Built-in функция, която добавя елементи към края на slice: 248 | 249 | sliceA := []int{1, 2, 3} 250 | sliceB := append(sliceA, 4, 5) // [1 2 3 4 5] 251 | 252 | Може да добавя и един slice към друг: 253 | 254 | sliceC := append(sliceA, sliceB...) 255 | 256 | Ако в резултатния slice има достатъчно място, той се използва непроменен. Ако няма, автоматично се заделя по-голям slice: 257 | 258 | sliceD := make([]int, 0, 3) // len = 0, cap = 3 259 | sliceD = append(sliceD, 1, 2) // len = 2, cap = 3 260 | sliceD = append(sliceD, 2, 4) // len = 4, cap = 6 261 | 262 | * Трик с append 263 | 264 | Изтриване на n-ия елемент от слайс 265 | 266 | x := []int{1, 2, 3, 4, 5} 267 | x = append(x[:n], x[n+1:]...) 268 | 269 | Ако n = 2: 270 | 271 | []int{1, 2, 4, 5} 272 | 273 | 274 | * copy(dst, src) 275 | 276 | - Копира елементи от един слайс в друг 277 | - Връща броя копирани елементи 278 | - Source и destination може да се припокриват 279 | 280 | * copy(dst, src) - примери 281 | 282 | var l int 283 | slice1 := []int{1, 2, 3, 4} 284 | slice2 := []int{7, 6, 5} 285 | 286 | Копираме трите елемента от `slice2` в `slice1` 287 | 288 | l = copy(slice1, slice2) // slice1 = [7 6 5 4], l = 3 289 | 290 | Копираме края на slice1 в началото му 291 | 292 | l = copy(slice1, slice1[2:]) // slice1 = [3 4 3 4], l = 2 293 | 294 | Копираме slice1 в slice2 295 | 296 | l = copy(slice2, slice1) // slice2 = [1 2 3], l = 3 297 | // Копират се само първите 3 елемента, защото len(slice2) = 3 298 | 299 | 300 | * slice "gotchas" 301 | 302 | 1. Опит за писане в неинициализиран слайс води до паника. 303 | 304 | 2. Масивите, в които се съхраняват данните на слайсовете, не се чистят от garbage collector-a, докато има референции (слайсове) към тях. 305 | 306 | // WARNING: shitty code, don't do this at home. We are professionals! 307 | func GetFileHeader(filename string) []byte { 308 | b, _ := ioutil.ReadFile(filename) 309 | return b[:10] 310 | } 311 | 312 | Цялото съдържание на файла няма да бъде изчистено от паметта, докато първите 10 байта се ползват някъде. 313 | 314 | Решение: copy() в нов слайс 315 | 316 | * Maps 317 | 318 | Неподредена колекция от двойки ключове и стойности 319 | 320 | var x map[string]int // Ключовете в x са низове, а стойностите числа 321 | 322 | 323 | За да го инициализраме, ползваме `make`: 324 | 325 | x := make(map[string]int) 326 | 327 | Подобно на слайсовете, писането в неинициализиран map води до паника. 328 | 329 | Ползваме го почти както масиви и слайсове. Добавяне на стойност: 330 | 331 | x["key"] = 10 332 | 333 | За да вземем стойност по ключ: 334 | 335 | value, ok := x["key"] 336 | 337 | `ok` е `true`, ако съществува двойка с такъв ключ. В противен случай, `value` е нулевата стойност на типа (`""` за `string`) и `ok` е false. 338 | 339 | 340 | * Полезнотии 341 | 342 | - Бързо инициализиране: 343 | 344 | wordcount := map[string]int{"word1": 10, "word2": 5} 345 | 346 | - Изтриването на стойност става с `delete`: 347 | 348 | x := make(map[string]int) 349 | delete(x, "key") // Изтрива двойката с ключ е "key". Ако няма такава, нищо не се случва. 350 | 351 | - Проверка дали даден ключ съществува: 352 | 353 | if _, ok := x["key"]; ok { 354 | fmt.Println("key exists") 355 | } 356 | 357 | - Итерирането става с `range`: 358 | 359 | for key, value := range m { 360 | fmt.Println("Key:", key, "Value:", value) 361 | } 362 | 363 | * Ключове 364 | 365 | - Трябва да а от "сравняем" тип 366 | 367 | * Сравняване? 368 | 369 | .link https://golang.org/ref/spec#Comparison_operators 370 | - Накратко (и непълно): bool, numeric, string, channel, interface са сравними 371 | - Масиви са сравними ако стойностите им са сравними и са равни когато всичките им стойности са равни 372 | - Указатели са сравними и са равни когато сочат към една и съща променлива 373 | - Структури са сравними, когато всичките им полета са сравними 374 | - Има несравними неща, има и такива, които ще доведат до runtime panic ако ги сравнявате 375 | - Прочетете поне веднъж спецификацията, където всичко е изброено пълно - кратка е 376 | 377 | * Конкурентен достъп 378 | 379 | - За конкурентност ще говорим по - нататък, но за сега запомнете следните неща 380 | - Ако се чете и пише едновременно в array, slice или map, поведенито е недефинирано 381 | - a.k.a. не са thread-safe 382 | - Често се случва паника 383 | 384 | * Конкурентен достъп (2) 385 | 386 | Но има [[https://golang.org/pkg/sync/#Map][sync.Map]] в стандартната библиотека, който позволява конкурентен достъп. 387 | 388 | * Structs 389 | 390 | - Добре познатите C структури 391 | - Контейнер, който съдържа полета от други типове 392 | 393 | type Person struct { 394 | name string 395 | age uint 396 | } 397 | 398 | var chochko Person 399 | chochko.name = "Чочко" 400 | chochko.age = 27 401 | 402 | Други начини за инициализиране: 403 | 404 | chochko := Person{name: "Чочко", age: 27} 405 | chochko := Person{"Чочко", 27} 406 | 407 | 408 | * new() 409 | 410 | - Алокира памет, която да използваме за дадения тип 411 | - Връща указател към нулирана, но не инициализирана памет 412 | 413 | chochko := new(Person) 414 | chochko.name = "Чочко" 415 | chochko.age = 27 416 | 417 | - chochko е *Person, но се използва по същия начин ('cause -> is so 80s) 418 | 419 | * new() vs. make() 420 | 421 | new само заделя и нулира памет, а make инициализира, т.е.: 422 | 423 | .play code/data_structures/new_vs_make.go 424 | 425 | - Демек `make` се ползва само върху `slice` и `map` 426 | 427 | * Следващия път 428 | 429 | - Типове и интерфейси 430 | -------------------------------------------------------------------------------- /04-types_and_interfaces.slide: -------------------------------------------------------------------------------- 1 | Типове и интерфейси 2 | 31.10.2018 3 | 4 | fmi@golang.bg 5 | http://fmi.golang.bg/ 6 | @fmi_golang 7 | 8 | * Но преди това... 9 | 10 | * Въпрос за мъфин #1 11 | 12 | Как увеличаваме броя елементи в един масив? Можем ли да сравняваме масиви с оператора `"=="` ? 13 | 14 | - Не може да променяме големината на масиви (само на slices) 15 | - Да, можем да сравняваме с `==` 16 | 17 | * Въпрос за мъфин #2 18 | 19 | arr := [6]float64{1, 2, 3, 4, 5, 6} 20 | x := arr[1:] 21 | y := append(x, 4) 22 | 23 | Какви типове имат `arr`, `x`, `y`? 24 | Какво ще върнат `len(x)`, `cap(x)`, `len(y)`, `cap(y)`? 25 | 26 | - [6]float64 27 | - []float64 28 | - []float64 29 | - 5 30 | - 5 31 | - 6 32 | - 10 33 | 34 | * Въпрос за мъфин #3 35 | 36 | Кои от следните типове са допустими ключове на map? 37 | 38 | - `string` 39 | - `[5]int` 40 | - `[]int` 41 | - `struct{int}` 42 | - `*[]int` 43 | 44 | * Въпрос за мъфин #3 45 | 46 | Отговор: 47 | 48 | - Всички без `[]int` 49 | - ...да, `*[]int` допустим ключ, макар и не много полезен: 50 | 51 | .play code/concurrency101/map-keys.go /^func main/, 52 | 53 | * Въпрос за мъфин #4 54 | 55 | Каква е разликата между `new()` и `make()`, кога се ползва едното и кога другото и какво връщат? 56 | 57 | - `new` само заделя памет и я нулира, за разлика от `make`, което инициализира обекта 58 | - `new` ползваме за наши типове (структури), а `make` за вградени типове като slices и maps 59 | - `new` връща указател, `make` връща стойност 60 | 61 | * Въпрос за мъфин #5 62 | 63 | Как инициализираме thread-safe slice или map? 64 | 65 | - Няма такова животно 66 | - Трябва сами да се погрижим за thread-safe достъп до тях 67 | 68 | * Типове и интерфейси 69 | 70 | 71 | 72 | * Собствени типове 73 | 74 | - От всеки тип в езика можем да създаваме наши типове 75 | - Не се различават по нищо от вградените 76 | 77 | type integer int 78 | type float float64 79 | type chars string 80 | 81 | - Не особено полезно, на пръв поглед 82 | - Но те могат и повече 83 | 84 | * Нека разгледаме функцията Abs 85 | 86 | func Abs(i integer) integer { 87 | switch { 88 | case i < 0: 89 | return -i 90 | case i == 0: 91 | return 0 92 | default: 93 | return i 94 | } 95 | } 96 | 97 | var number integer = -42 98 | positiveInteger := Abs(number) 99 | 100 | - Така се дефинира обикновена функция `Abs`, която се извиква като ѝ се подаде integer като аргумент 101 | - Това не е "обектно-ориентираният" начин да се направи подобно нещо 102 | 103 | 104 | * "Обектно-ориентираният" начин да се направи подобно нещо 105 | 106 | func (i integer) Abs() integer { 107 | switch { 108 | case i < 0: 109 | return -i 110 | case i == 0: 111 | return 0 112 | default: 113 | return i 114 | } 115 | } 116 | 117 | var number integer = -42 118 | number.Abs() 119 | 120 | 121 | * Какво точно е метод? 122 | 123 | - Методите се изпълняват върху конкретен тип 124 | - Той се нарича receiver 125 | - Методите могат да се дефинират *само* върху типове, дефинирани в същия пакет 126 | - Като следствие от горното правило, методите имат достъп до private полетата в типа 127 | - На практика, дефинират държанието на типа 128 | 129 | 130 | * Що е то receiver-а? 131 | 132 | - Няма фиксирана ключова дума за това. 133 | - Има просто конвенция (първата/ите букви от името на типа) 134 | - Той може да бъде по стойност, както и указател 135 | 136 | По стойност 137 | 138 | - Работи се върху копие на обекта 139 | - Това може да е скъпа операция за големи обекти 140 | 141 | Като указател 142 | 143 | - Работи се върху копие на указателя към обект 144 | - Всяка промяна в метода се отразява на оригиналния обект 145 | 146 | Няма различен синтаксис за използването на двата вида receiver-и. 147 | 148 | 149 | * Пример 150 | 151 | .play code/types_and_interfaces/integer.go /^type/,/end/ 152 | 153 | * nil обекти 154 | 155 | Извикването на методи работи дори с nil pointers към обекти: 156 | 157 | .play code/types_and_interfaces/nil_objects.go /^type/,/end/ 158 | 159 | Внимавайте с тях :) 160 | 161 | 162 | * struct 163 | 164 | - Същите неща могат да се правят и върху композитни типове 165 | - Полета и методи с малка буква са private 166 | - Нека си дефинираме типове за триъгълник и правоъгълник 167 | 168 | .code code/types_and_interfaces/shapes.go /start types/,/end types/ 169 | 170 | 171 | * Методи за тези типове 172 | 173 | .code code/types_and_interfaces/shapes.go /start methods/,/end methods/ 174 | 175 | 176 | * "Завързани" методи 177 | 178 | .play code/types_and_interfaces/bound_methods.go /^type/, 179 | 180 | 181 | * Интерфейси 182 | 183 | - Какво правим, ако искаме да имаме списък от геометрични фигури, на които ще търсим обиколка или лице? 184 | - Тези типове нямат общ "родител" 185 | - Няма нужда и да са в един пакет 186 | - Това в Go се постига с интерфейси 187 | - Интерфейсите са както съвкупност от методи, така и тип от езика 188 | - Общият интерфейс на двете фигури са методите `Circumference()` и `Area()` 189 | - Нашите фигури нямат представа, че такъв интерфейс съществува 190 | 191 | * Stringer 192 | 193 | type Stringer interface { 194 | String() string 195 | } 196 | 197 | Това е интерфейс от стандартната библиотека, дефиниран в пакета `fmt`. 198 | 199 | Ако имплементираме този интефейс за наш тип, може да променим начина, по който `fmt.Printf()` го принтира чрез `%s`. 200 | 201 | .play code/types_and_interfaces/stringer.go /start/,/end/ 202 | 203 | * Структура 204 | 205 | type Binary uint64 206 | 207 | 208 | .image assets/interface0.png 209 | 210 | .image assets/interface.png 211 | 212 | По-подробно обяснение може да намерите тук: [[http://research.swtch.com/interfaces]] 213 | 214 | * Дефиниция на интерфейс 215 | 216 | .code code/types_and_interfaces/shapes.go /start interface/,/end interface/ 217 | 218 | - Това е нов абстрактен тип, от който не можем да създаваме обекти, но можем да имаме променливи от него 219 | - Всеки тип, който има методите `Circumference` и `Area` със същата сигнатура, имплементира `Shape` 220 | - Двата типа `Triangle` и `Rectangle` *имплицитно* го имплементират 221 | - Променливите от тип интерфейс са първокласни обекти в Go 222 | 223 | 224 | * Пример 225 | 226 | .play code/types_and_interfaces/shapes.go /start funcs/,/end funcs/ 227 | 228 | 229 | * Вложени типове 230 | 231 | - Можем да накараме един тип да присвои държанието на друг тип 232 | - Това не е наследяване в смисъла на OOP 233 | - Влагащият тип не е от тип вложения (демек няма is-a релация) 234 | - Просто получава всички негови полета и методи 235 | - Има два начина да ги използваме 236 | 237 | 238 | * Композиция 239 | 240 | _Конструираме_един_тип,_комбинирайки_няколко_прости_други_типa._ 241 | 242 | *** Пример: 243 | Искаме да си направим smartphone. Не откриваме топлата вода, а просто го наблъскваме с каквито джаджи се сетим. 244 | 245 | type Smartphone struct { 246 | phone BasicPhone 247 | camera CameraModule 248 | wifi WiFiModule 249 | screen MultiTouchScreen 250 | battery DamnHugeBattery 251 | gyroscope SmallGyroscope 252 | gps GPSModule 253 | secret CanOpener 254 | } 255 | 256 | Всеки един от тези типове отговаря за точно едно нещо и може да бъде използвано самостоятелно. 257 | 258 | 259 | * Квази-Наследяване 260 | 261 | Вярваме, че знаете как работи то. Дори сме сигурни, че сте правили хора и студенти: 262 | 263 | .play code/types_and_interfaces/anon_structs.go /start/,/end/ 264 | 265 | Вложеният тип Person е анонимен, което присвоява всичките му методи и атрибути на базовия тип. 266 | 267 | * Множествено "наследяване" 268 | 269 | Да, имате право на много анонимни вложени типа. 270 | 271 | Не, това не е яко. 272 | 273 | Да, не очакваме да го ползвате често. 274 | 275 | 276 | * Duck typing 277 | 278 | Всеки обект имплементира празния интерфейс: 279 | 280 | interface{} 281 | 282 | Съответно на променлива от такъв тип може да присвоим всякаква стойност! 283 | 284 | Но с нея не можем да правим абсолютно нищо :) 285 | 286 | Това може да звучи безполезно, но не е, тъй като имаме... 287 | 288 | 289 | * Type Assertion 290 | 291 | .play code/types_and_interfaces/type_assertion.go 292 | 293 | В `str` ще имаме стойността на `value`, ако тя наистина е била от тип `string`. 294 | 295 | 296 | * Type Switch 297 | 298 | .play code/types_and_interfaces/interface_conversions.go /^type mystruct/, 299 | 300 | 301 | Така може да правим различни неща въз основа на типа на променлива. 302 | 303 | 304 | * JSON 305 | 306 | .play code/types_and_interfaces/plain_json.go /^type Rectangle/, 307 | 308 | 309 | * JSON 310 | 311 | type Marshaler interface { 312 | MarshalJSON() ([]byte, error) 313 | } 314 | 315 | type Unmarshaler interface { 316 | UnmarshalJSON([]byte) error 317 | } 318 | 319 | * JSON 320 | 321 | 322 | .play code/types_and_interfaces/custom_json.go /start/,/end/ 323 | 324 | 325 | * Type Aliases 326 | 327 | type integerAlias = int 328 | 329 | *Не* създава нов тип, a псевдоним на вече съществуващ тип 330 | 331 | .play -edit code/types_and_interfaces/type_aliases.go /START OMIT/,/END OMIT/ 332 | -------------------------------------------------------------------------------- /05-concurrency101.slide: -------------------------------------------------------------------------------- 1 | Concurrency 101 2 | 07.11.2018 3 | 4 | fmi@golang.bg 5 | http://fmi.golang.bg/ 6 | @fmi_golang 7 | 8 | * Но преди това... 9 | 10 | * Въпрос за мъфин #1 11 | 12 | Какво е интерфейс? 13 | 14 | - Тип от езика 15 | - Съвкупност от дефиниции на методи 16 | 17 | * Въпрос за мъфин #2 18 | 19 | Как се създават нови типове? 20 | 21 | - С ключовата дума `type` 22 | - Ситанксиса е `type Name ` 23 | 24 | * Въпрос за мъфин #3 25 | 26 | type A struct { 27 | a int 28 | } 29 | type B struct { 30 | a float64 31 | } 32 | type C struct { 33 | A 34 | B 35 | } 36 | var c C 37 | 38 | 39 | Какво е `c.a`? 40 | 41 | Отговор: 42 | - Компилатора ще ни каже грешка защото не знае кое `a` имаме предвид. 43 | - `c.A.a` е int 44 | - `c.B.a` е float64 45 | 46 | 47 | * Въпрос за мъфин #4 48 | 49 | По какъв начин показваме, че тип имплементира интерфейс? 50 | 51 | - Никак 52 | - Типовете имплементират интерфейси неявно 53 | 54 | * Какво ще говорим днес? 55 | 56 | - Конкурентност с/у Паралелизъм 57 | - Кратка история на многопроцесорното програмиране 58 | - go рутини 59 | - Споделяне чрез комуникация 60 | - channels 61 | 62 | * Що е то конкурентност? 63 | 64 | * Магически паралелизъм? 65 | 66 | - Go е конкурентен 67 | - ... следователно всичко ще работи паралелно! Yey! 68 | - Не е точно така, може би бъркате дефинициите си. 69 | 70 | * Конкурентност 71 | 72 | - Конкурентност е да се *занимаваш* с много неща "едновременно" 73 | - Имаме набор от независими процеси/задачи 74 | - Могат да започват, работят и приключват в припокриващи се периоди 75 | - Програмите в една операционна система, работеща на 1 процесорно ядро, са конкурентни 76 | - В JavaScript има concurrency, без да е необходим паралелизъм 77 | 78 | * Паралелизъм 79 | 80 | - *Изпълнение* на различни неща едновременно 81 | - Едновременно пресмятане на една задача от няколко процеса 82 | 83 | * Конкурентност с/у Паралелизъм 84 | 85 | - Когато говорим за конкурентност става въпрос за структурата на програмата 86 | - Когато говорим за паралелизъм става въпрос за изпълнението ѝ 87 | 88 | * Обяснение с малко повече gophers 89 | 90 | - Лекция на Rob Pike по въпроса: 91 | 92 | .link http://blog.golang.org/concurrency-is-not-parallelism 93 | 94 | .link https://talks.golang.org/2012/waza.slide 95 | 96 | * CPU скорост vs. производителност 97 | 98 | .image https://i.stack.imgur.com/z94Of.png 400 _ 99 | 100 | - Moore's law 101 | - А какво става, когато имаме много ядра? 102 | 103 | 104 | * IO-bound vs. CPU-bound 105 | 106 | - CPU-bound са програми, които главно зависят от време, прекарано в процесора 107 | - IO-bound са програми, които главно зависят от време, прекарано в чакане на мрежата, паметта или диска 108 | 109 | 110 | * Подходи за конкурентност 111 | 112 | - Процеси 113 | - Нишки (native & green) 114 | - Актьори 115 | - Мега умни компилатори? 116 | 117 | А как синхронизираме различните задачи? 118 | 119 | 120 | * В C ползват вилици 121 | 122 | .code code/concurrency101/c_fork.c 123 | 124 | - `fork` създава ново копие на програмата, която изпълняваме 125 | - Всички ресурси и променливи запазват стойността си в процеса-дете 126 | - След създаването на новия процес, всички промени са локални 127 | - Все едно клонираме хора, за да вършим повече работа едновременно 128 | 129 | 130 | * Синхронизация на вилици 131 | 132 | .code code/concurrency101/c_fork_sync.c 133 | 134 | - `execl` спира изпълнението на текущия процес и зарежда друг 135 | - `waitpid` позволява на родителя да чака свършването на конкретно дете 136 | 137 | * Предимства и недостатъци на fork 138 | 139 | Против: 140 | 141 | - Само за UNIX 142 | - Създаването на нов процес е бавно и паметоемко 143 | - Комуникацията между процеси е трудна - нямат обща памет 144 | - Копира се памета на процеса 145 | 146 | За: 147 | 148 | - Копира се памета на процеса 149 | - Стабилност 150 | - Детето е независимо - ако омаже нещо, родителят няма да пострада 151 | 152 | 153 | * В Go се правим на модерни 154 | 155 | - Fork не се препоръчва 156 | - Имаме по - добър начин, за него след малко 157 | - Ако все пак искате чрез библиотеката `syscall` можете да вдигнете нов процес 158 | - Не го правете, ако нямате много сериозна причина 159 | 160 | 161 | * Нишки 162 | 163 | - Много нишки живеят в един и същи процес 164 | - Следователно имат достъп до една и съща памет 165 | - Глобалните променливи са общи за нишките 166 | - Създават се бързо и лесно 167 | - Това е концепция в операционните системи 168 | - Някои езици ги поддържат директно 169 | - Други ги скриват зад ниво на абстрактност 170 | 171 | * Нишки в C 172 | 173 | .code code/concurrency101/c_threads.c /ticker/, 174 | 175 | * Нишки в Python 176 | 177 | .code code/concurrency101/python_threads.py /ticker/,/thread.start/ 178 | 179 | или 180 | 181 | .code code/concurrency101/python_threads.py /class/,/thread.join/ 182 | 183 | * Goroutines 184 | 185 | 186 | * Скучно 187 | 188 | За да се съсредоточим върху това, което се опитваме да кажем ще дадем скучен пример. 189 | 190 | .play code/concurrency101/boring.go /^func main/, 191 | 192 | За конкурентноста таймингът е важен. Нека е малко по - непредвидим. 193 | 194 | 195 | * Малко по - малко скучно 196 | 197 | Ще сложим случайно време за сън. 198 | 199 | .play code/concurrency101/less-boring.go /^func main/, 200 | 201 | Скучната ни програма ще продължи да работи така до безкрайност. Като много скучна лекция, от която ви е неудобно да си тръгнете. 202 | 203 | 204 | * Да я игнорираме 205 | 206 | Скучната програма не заслужава вниманието ни, нека не я чакаме. 207 | 208 | С `go` пускаме функция нормално, но пускащия няма нужда чака приключването й. 209 | 210 | Пускаме goroutine. 211 | 212 | .play code/concurrency101/go-less-boring.go /^func main/, 213 | 214 | Когато main приключи програмата спира. 215 | 216 | 217 | * Да я игнорираме малко по - малко 218 | 219 | .play code/concurrency101/go-less-boring-sleep.go /^func main/, 220 | 221 | Изпълнявахме main и скучната функция едновременно. 222 | 223 | С края на main дойде и края на скучната функция. 224 | 225 | 226 | * Какво е Goroutine 227 | 228 | - Независимо изпълняваща се функция 229 | - Практически безплатни са за създаване от към памет и процесорно време. Може да имате стотици хиляди в един процес 230 | - Не е thread 231 | - Зелени нишки 232 | - Има умен scheduler, който мапва горутини към OS нишки 233 | - Но ако мислите за тях като за много евтини нишки, няма да сте далеч от истината 234 | - Дизайнът на езика и особено go рутините са много повлияни от Communicating sequential processes на C. A. R. Hoare 235 | 236 | .link http://usingcsp.com/cspbook.pdf 237 | 238 | * Вдъхновено от 239 | 240 | - Последните няколко примера са безсрамно присвоени от лекция на Rob Pike. Интересна е, препоръчваме я. 241 | 242 | .link http://www.youtube.com/watch?v=f6kdp27TYZs 243 | 244 | - А сега да се върнем към нишки и goroutines 245 | 246 | 247 | * Проблеми, свързани с нишки 248 | 249 | От това, че имат една и съща памет, следва, че могат да достъпват едни и същи променливи 250 | 251 | int i = 0 252 | 253 | thread1 { i++ } 254 | thread2 { i++ } 255 | 256 | wait { thread1 } { thread2 } 257 | print i 258 | 259 | Тук `i` накрая може да бъде 1 или 2. 260 | 261 | 262 | * Критични секции 263 | 264 | - Части от кода, които могат да бъдат изпълнени само от една нишка/процес в даден момент, се наричат критични секции 265 | - Те са основна част от многозадачното програмиране 266 | - Има много похвати за реализирането на критични секции 267 | - STM, Semaphores & Co., Message passing, Actors 268 | 269 | В Go имаме Semaphores и Message passing 270 | 271 | * Communicate by sharing vs. Share by communicating 272 | 273 | - Може експлицитно да използваме "ключалки", за да ограничаваме едновременния достъп до споделена памет 274 | - В Go също може да го правим, подобно на повечето mainstream езици 275 | - Но в много ситуации в Go би било по-добре да споделяме памет чрез комуникация 276 | - Може да предаваме (референции към) данни между различни горутини с помощта на канали 277 | 278 | * Communicate by sharing vs. Share by communicating 279 | 280 | .image assets/gopher-02-door-communication.jpg 281 | 282 | * Channels 283 | 284 | - Вграден тип, който се използва за комуникация между различни горутини 285 | - Може да се използва и за синхронизация 286 | - За тях има специален синтаксис 287 | 288 | 289 | * Употреба на канали 290 | 291 | - Инстанцират се с `make`, като се подава типа, който ще се пренася 292 | - Този е за пренасяне на цели числа: 293 | intChannel := make(chan int) 294 | 295 | - Могат да бъдат буферирани и небуферирани 296 | - По подразбиране са небуферирани 297 | - Ето буфериран канал за пренасяне на string slices: 298 | 299 | ch := make(chan []string, 100) 300 | 301 | - В канал може да се изпраща и от него може да се получава 302 | 303 | ch <- 64 304 | read := <-ch 305 | 306 | - Изпращането и получаването може да блокират докато някой "отсреща" не извърши "противоположната" операция 307 | 308 | 309 | * IO в канал 310 | 311 | Операциите по изпращане и получаване се изпълняват с оператора `<-` 312 | 313 | - `chan`<-`someValue` изпраща по канала 314 | - `someVar,`ok` = `<-chan` получава от канала 315 | 316 | Simple demo: 317 | 318 | .play code/concurrency101/channel-simple-demo.go /^func main/, 319 | 320 | * Затваряне 321 | 322 | Канал може да бъде затворен: 323 | 324 | close(ch) 325 | 326 | - Повече не може да бъде отворен 327 | - Писането в него води до паника 328 | - Четенето в него никога не блокира 329 | - Може да прочетете всички вече буферирани стойности, ако каналът е бил буфериран 330 | - След това четенето връща нулевата стойност за предавания тип и false като втори резултат 331 | 332 | 333 | * Каналите са първокласни обекти в Go 334 | 335 | - По канал може да пренасяте канал 336 | c := make(chan chan int) 337 | 338 | - Каналите могат да се подават като параметри на функции 339 | func doSomething(input chan string) { 340 | // do something 341 | } 342 | 343 | - Функциите могат да връщат канали като резултат. 344 | func doSomethingElse() chan string { 345 | result := make(chan string) 346 | return result 347 | } 348 | 349 | * range 350 | 351 | Помните ли как ви казахме, че `range` е нещо супер яко? 352 | 353 | - Може да чете и от канали 354 | - Блокира, докато не получи следващата стойност 355 | - Излизаме от `for`, когато каналът бъде затворен 356 | 357 | for val := range ch { 358 | fmt.Printf("Recieved: %#v\n", val) 359 | } 360 | 361 | 362 | * Ограничени канали 363 | 364 | - Каналите могат да бъдат само за четене (`<-chan`) или само за писане (`chan<-`) 365 | - Това е по-полезно, отколкото звучи: 366 | 367 | .play code/concurrency101/generator.go /func randomFeed/, 368 | 369 | 370 | * Deadlock 371 | 372 | .play code/concurrency101/deadlock.go /func main/, 373 | 374 | 375 | * nil channel 376 | 377 | Никога не използвайте неинициализиран канал! 378 | 379 | - Писането в него блокира завинаги 380 | 381 | package main 382 | 383 | func main() { 384 | var c chan string 385 | c <- "ping" // deadlock 386 | } 387 | 388 | - Четенето от него... блокира завинаги 389 | 390 | package main 391 | 392 | import "fmt" 393 | 394 | func main() { 395 | var c chan string 396 | fmt.Println(<-c) // deadlock 397 | } 398 | 399 | * Пример за синхронизация 400 | 401 | .play code/concurrency101/synchronization.go /func main/, 402 | 403 | - Не използвайте int или bool ако просто използвате канала за синхронизация. 404 | - Използвайте struct{} за целта - безплатно от гледна точка на памет. 405 | 406 | * По-сложен пример 407 | 408 | var sem = make(chan struct{}, MaxOutstanding) 409 | 410 | func init() { 411 | for i := 0; i < MaxOutstanding; i++ { 412 | sem <- struct{}{} 413 | } 414 | } 415 | 416 | func handle(r *Request) { 417 | <-sem 418 | process(r) 419 | sem <- struct{}{} 420 | } 421 | 422 | func Serve(queue chan *Request) { 423 | for { 424 | req := <-queue 425 | go handle(req) 426 | } 427 | } 428 | 429 | 430 | * Затваряне на канали 431 | 432 | .play code/concurrency101/closing-channels.go 433 | 434 | * Домашно 435 | -------------------------------------------------------------------------------- /06-concurrency102.slide: -------------------------------------------------------------------------------- 1 | Concurrency 102 2 | 14.11.2018 3 | 4 | fmi@golang.bg 5 | http://fmi.golang.bg/ 6 | @fmi_golang 7 | 8 | * Но преди това... 9 | 10 | - Изберете си проект. Краен срок: Бъдни вечер 11 | - Първи тест на 28.11.2018 (по - следващата седмица) 12 | - Днес ще пуснем ново домашно 13 | 14 | * Въпроси за мъфин #1 15 | 16 | - Какво е канал в го и трябва ли ви водопроводчик? 17 | 18 | Тип в езика 19 | Служи за комуникация между go рутини 20 | В него може да се четат и пишат стойности от конкретен тип 21 | 22 | * Въпрос за мъфин #2 23 | 24 | - Какъв ще е резултатът от кода? 25 | 26 | func worker(val string) { 27 | fmt.Prinln(val) 28 | } 29 | 30 | func main() { 31 | go worker("one") 32 | go worker("two") 33 | worker("three") 34 | time.Sleep(10 * time.Second) 35 | } 36 | 37 | Не е ясно. Нещата се изпълняват конкурентно. 38 | 39 | * Въпрос за мъфин #3 40 | 41 | - Кога завършва вашия процес? 42 | 43 | Когато главната go рутина завърши. 44 | Независимо колко други живи има. 45 | 46 | * Въпрос за мъфин #4 47 | 48 | - Какво става когато четеш от `nil` канал? Когато пишеш? 49 | 50 | Четене: блокира за винаги 51 | Писане: блокира за винаги 52 | 53 | * Въпрос за мъфин #5 54 | 55 | - Ами за затворен канал? А ако се опиташ да го затвориш отново? 56 | 57 | Четене: връща нулевата стойност на типа на канала 58 | Писане: паника 59 | Затваряне: паника 60 | 61 | * Concurrency 102 62 | 63 | * Ще си говорим за 64 | 65 | - sync 66 | - select 67 | - patterns 68 | 69 | * Да си спомним скуката 70 | 71 | .play code/concurrency102/go-less-boring-sleep.go /^func main/,/^}/ 72 | 73 | - Грозен `time.Sleep` 74 | - Ако искаме да гледаме скуката точно определен брой пъти? 75 | 76 | * sync 77 | 78 | [[https://golang.org/pkg/sync/][sync]] е пакет, който ни дава синхронизационни примитиви от сравнително ниско ниво: 79 | 80 | - `Cond` 81 | - `Mutex` и `RWMutex` 82 | - `Once` 83 | - `Pool` 84 | - `WaitGroup` 85 | 86 | един полезен интерфейс: 87 | 88 | type Locker interface { 89 | Lock() 90 | Unlock() 91 | } 92 | 93 | и подпакет `atomic` с low-level memory примитиви 94 | 95 | * WaitGroup 96 | 97 | Изчаква колекция от горутини да приключат и чак тогава продължава с изпълнението. 98 | Така не правим простотии със `time.Sleep`, както преди. 99 | 100 | type WaitGroup struct {} 101 | 102 | func (*WaitGroup) Add(delta int) 103 | func (*WaitGroup) Done() 104 | func (*WaitGroup) Wait() 105 | 106 | * Пример 107 | 108 | .play code/concurrency102/boring-with-sync.go /^func main/,/^}/ 109 | 110 | Стига вече с тая скука! 111 | 112 | * По - интересен пример 113 | 114 | .play code/concurrency102/waitgroup.go /^func main/,/^}/ 115 | 116 | 117 | * Mutex 118 | 119 | type Mutex struct {} 120 | 121 | func (*Mutex) Lock() 122 | func (*Mutex) Unlock() 123 | 124 | - Дава достъп до даден ресурс само на една горутина по едно и също време 125 | - Ако втора се опита да го достъпи, тя чака, докато ресурсът не бъде освободен 126 | - Ако много горутини чакат за един ресурс, достъп се дава на една от тях на случаен принцип 127 | - Има смисъл да се ползва като `private` атрибут на наш тип 128 | - `Unlock()` е добра идея да бъде в `defer` 129 | - Имплементира интерфейса `sync.Locker` 130 | 131 | * В код 132 | 133 | .play code/concurrency102/mutex.go /^func main/,/^}/ 134 | 135 | * На каналски 136 | 137 | .play code/concurrency102/mutex_chan.go /^func main/,/^}/ 138 | 139 | * RWMutex 140 | 141 | - Дава thread-safe достъп до споделен ресурс (променлива) от множество горутини 142 | - Дава достъп до ресурса в даден момент само на *един* writer или на произволен брой readers 143 | - По-ефективен от `Mutex` при ситуации, в които част от горутините само четат 144 | 145 | type RWMutex struct {} 146 | func (*RWMutex) Lock() 147 | func (*RWMutex) Unlock() 148 | func (*RWMutex) RLock() 149 | func (*RWMutex) RUnlock() 150 | func (*RWMutex) RLocker() sync.Locker 151 | 152 | - Също имплементира `sync.Locker` 153 | - Може да бъде "държана" от произволен брой "четци" или точно един "писар" 154 | - `RLock` блоква само докато някой използва `Lock` 155 | - Ако извикате някой unlock на незаключен mutex - run-time error 156 | - `RLocker()` връща `Locker`, който ще използва `RLock` и `RUnlock` на оригинала 157 | 158 | * Once 159 | 160 | Обект от този тип ще изпълни точно една функция. 161 | 162 | .play code/concurrency102/once.go /^func main/,/^}/ 163 | 164 | * Cond 165 | 166 | type Cond struct { 167 | L Locker // this is held while observing or changing the condition 168 | // other unexported fields ... 169 | } 170 | 171 | func NewCond(l Locker) *Cond 172 | 173 | func (c *Cond) Wait() 174 | func (c *Cond) Broadcast() 175 | func (c *Cond) Signal() 176 | 177 | - Нарича се `condition-variable` или `monitor` 178 | - Синхронизира горутини на принципа на съобщаването за настъпване на дадено събитие 179 | - Напълно в реда на нещата е да бъде част от наша структура 180 | - *Никога* не трябва да го копирате след като решите да го ползвате! 181 | 182 | * Cond Demo 183 | 184 | Demo `./code/concurrency102/cond.go` 185 | 186 | * select 187 | 188 | * select 189 | 190 | select { 191 | case v1 := <-c1: 192 | fmt.Printf("received %v from c1\n", v1) 193 | case v2 := <-c2: 194 | fmt.Printf("received %v from c2\n", v2) 195 | case c3 <- 5: 196 | fmt.Println("send 5 to c3") 197 | default: 198 | fmt.Printf("no one was ready to communicate\n") 199 | } 200 | 201 | 202 | Накратко: `switch` за канали. 203 | Надълго: изчаква първия case, по който е изпратена или получена стойност 204 | 205 | - Ако по никой от каналите няма "движение", изпълнява `default` 206 | - Ако няма `default` блокира и изчаква 207 | - Няма определен ред: ако няколко не-default case-а са възможни, избира се случаен 208 | - Важна част от различни concurrency patterns в Go 209 | 210 | * Въпрос 211 | 212 | Какво би направил следния код? 213 | 214 | .play code/concurrency102/indeterminate-select.go /^func main/, 215 | 216 | Не се знае. 217 | 218 | * Concurrency patterns 219 | 220 | * Timeout 221 | 222 | .play code/concurrency102/timeout.go /^func fetch/, 223 | 224 | 225 | * Игра на развален телефон 226 | 227 | .image assets/gophereartrumpet.jpg 228 | 229 | - или колко точно са бързи и евтини горутините? 230 | 231 | * Игра на развален телефон 232 | 233 | .play code/concurrency102/daisy-chain.go /START/,/END/ 234 | 235 | 236 | * Generators 237 | 238 | .play code/concurrency102/fib.go 239 | 240 | 241 | * Fan In 242 | 243 | .play code/concurrency102/dummy_fanin.go /^func talk/, 244 | 245 | - Какъв е проблемът тук? 246 | 247 | * Fan In 248 | 249 | .image assets/fanin.jpg 250 | 251 | * Fan In <- Concurrency 252 | 253 | .play code/concurrency102/select_fanin.go /func fanIn/, 254 | 255 | * Finish channel 256 | 257 | .code code/concurrency102/select_finish_fanin.go /FANIN START/,/FANIN END/ 258 | 259 | * Finish channel 260 | 261 | .play code/concurrency102/select_finish_fanin.go /MAIN START/,/MAIN END/ 262 | 263 | * Words of wisdom 264 | 265 | - Всичко това е забавно, но не ставайте човека с чука 266 | - Го рутините и каналите са инструменти за построяване на програми 267 | - Но понякога ви трябва просто брояч 268 | - Инструменти за малките проблеми могат да бъдат намерени в "sync" и "sync/atomic" 269 | - Често всички тези неща ще работят заедно, за да се справят с по - големите проблеми 270 | - Използвайте правилния инструмент за задачата 271 | 272 | * И отново: 273 | 274 | - Изберете си проект. Краен срок: Бъдни вечер 275 | - Първи тест на 28.11.2018 276 | - Домашно днес 277 | 278 | * Следващия път 279 | 280 | Тестове и обработване на грешки 281 | -------------------------------------------------------------------------------- /08-homework-retrospective.slide: -------------------------------------------------------------------------------- 1 | За домашното 2 | 28.11.2018 3 | 4 | fmi@golang.bg 5 | http://fmi.golang.bg/ 6 | @fmi_golang 7 | 8 | * Подготвили сме ви тестове 9 | 10 | - Не се притеснявайте 11 | - Не сме ги забравили 12 | - В началото на следващия час 13 | 14 | * Но преди това... 15 | 16 | * Изберете си проект 17 | 18 | - Все още никой от вас не е предложил проект 19 | - Имате време до Бъдни вечер 20 | - Ако ви е страх, ще дам пример 21 | 22 | * Последното домашно 23 | 24 | - Да си поговорим за някои грешки 25 | 26 | * go get 27 | 28 | - `go get public/package/path` 29 | - Сваля ви този пакет в `$GOPATH` 30 | - Евентуално го инсталира 31 | - Demo 32 | 33 | * Interfaces 34 | 35 | - pointer receiver vs value reciever 36 | - Demo 37 | 38 | * Четете условието 39 | 40 | - Когато в условието изрично пише "ръбът е част от фигурата"... 41 | - ... сигурно ще го тестваме 42 | 43 | * Време е за теста 44 | 45 | * Как ще протече? 46 | 47 | - Раздаваме тестове на всички 48 | - Започвате заедно и имате 45 минути 49 | - Ако сте готови - предавате и си тръгвате 50 | - След края на времето чакате да съберем тестовете 51 | - Чак след тогава си тръгвате 52 | 53 | -------------------------------------------------------------------------------- /09-tools.slide: -------------------------------------------------------------------------------- 1 | Go Tools 2 | 05.12.2018 3 | 4 | fmi@golang.bg 5 | http://fmi.golang.bg/ 6 | @fmi_golang 7 | 8 | * Въпроси за "мъфини" 9 | 10 | * Въпрос за мъфин #1 11 | 12 | * Въпрос за мъфин #2 13 | 14 | * Въпрос за мъфин #3 15 | 16 | * Въпрос за мъфин #4 17 | 18 | * И по същество... 19 | 20 | * Tools 21 | 22 | * The go tool 23 | 24 | .image assets/go-tool-all-the-things.jpg 25 | 26 | * The Go Toolkit 27 | 28 | - Go има богата екосистема с инструменти (както вградени, така и външни) 29 | - Едно от най-ценните неща в езика 30 | - Лесно се пишат [[https://github.com/golang/go/tree/master/src/cmd][нови инструменти]]: има пакет [[https://golang.org/pkg/go/][go]], съдържащ пакети `ast`, `parser`, `build` и др. 31 | 32 | Вече сме говорили за някои от вградените инструменти: 33 | 34 | - `go`build` , `go`run` , `go`install` и `go`get` 35 | - `go`test` 36 | - `go`fmt` 37 | - Останалите може да видите чрез `go`-h` 38 | - Допълнителни официални инструменти [[https://godoc.org/golang.org/x/tools]] 39 | 40 | * Test coverage 41 | 42 | - Вграден инструмент от Go 1.5 43 | - Можем да видим колко от кода ни се тества: 44 | 45 | go test -cover 46 | 47 | - Ако искаме да знаем и кои части от кода са покрити от тестове: 48 | 49 | go test -coverprofile 50 | go tool cover -html -o 51 | 52 | - С `covermode` опцията може да определим как се смята покритието. Възможните опции са set, count и atomic 53 | 54 | * Примерен HTML output 55 | 56 | .image assets/code-coverage-html.png _ 1000 57 | 58 | * Race condition detection 59 | 60 | - Go идва с напълно интегриран в toolchain-а race condition detector 61 | - Runtime-а може да проверява за несинхронизиран достъп до споделени променливи и да се оплаква 62 | 63 | $ go test -race mypkg // test the package 64 | $ go run -race mysrc.go // compile and run the program 65 | $ go build -race mycmd // build the command 66 | $ go install -race mypkg // install the package 67 | 68 | - Обикновено се използва при тестовете, тъй като хаби доста CPU и памет 69 | - Повече информация: [[https://blog.golang.org/race-detector]] 70 | 71 | * goimports 72 | 73 | go get -u golang.org/x/tools/cmd/goimports 74 | 75 | - gofmt replacement, който също автоматично ви оправя import-ите 76 | - Маха ненужни import-и и добавя липсващи 77 | 78 | goimports -w . 79 | 80 | * go fix 81 | 82 | Спомняте ли си как ви казахме, че след версия 1.0 авторите са обещали, че каквото работи на 1.x ще работи на 1.y, където x < y ? 83 | 84 | Е да ама ... понякога се налага да се променят основни API-та. 85 | 86 | Ако след ъпдейт на go не ви се компилира проекта, можете просто да рънете: 87 | 88 | go fix . 89 | 90 | Все пак понякога се правят по специални промени, за които това няма да помогне. 91 | 92 | * go vet 93 | 94 | Статичен анализатор на go код, който може и да ви спести доста главоболия. Проверява за често срещани грешки във вашия код и се оплаква, ако ги намери. 95 | 96 | Някои поддържани проверки: 97 | 98 | - грешни аргументи на методи от вида на Printf 99 | - грешна сигнатура на някои често имплементирани методи от стандартните интерфейси 100 | - грешни тагове на полета в struct 101 | - недостижим код 102 | - чести грешки в ползване на sync/atomic 103 | - shadowed variables 104 | - и други 105 | 106 | * go doc 107 | 108 | - Позволява ви да гледате документация на сорс, който имате локално 109 | - Command line 110 | 111 | $ go doc github.com/ironsmile/httpms/src/webserver.BasicAuthHandler 112 | type BasicAuthHandler struct { 113 | // Has unexported fields. 114 | } 115 | BasicAuthHandler is a handler wrapper used for basic authenticate. Its only 116 | job is to do the authentication and then pass the work to the Handler it 117 | wraps around 118 | 119 | 120 | func (hl BasicAuthHandler) ServeHTTP(writer http.ResponseWriter, req *http.Request) 121 | 122 | * godoc 123 | 124 | - Може да се използва като `go`doc` за command line документация 125 | 126 | $ godoc github.com/ironsmile/httpms/src/webserver BasicAuthHandler 127 | use 'godoc cmd/github.com/ironsmile/httpms/src/webserver' for documentation on the github.com/ironsmile/httpms/src/webserver command 128 | 129 | type BasicAuthHandler struct { 130 | // contains filtered or unexported fields 131 | } 132 | BasicAuthHandler is a handler wrapper used for basic authenticate. Its 133 | only job is to do the authentication and then pass the work to the 134 | Handler it wraps around 135 | 136 | func (hl BasicAuthHandler) ServeHTTP(writer http.ResponseWriter, req *http.Request) 137 | ServeHTTP implements the http.Handler interface and does the actual 138 | basic authenticate check for every request 139 | 140 | 141 | * godoc 142 | 143 | - Но може и да пусне локален HTTP server, на който да гледате документация в красиви странички 144 | 145 | godoc -http=:6060 146 | 147 | Demo 148 | 149 | * golint 150 | 151 | "Официален" linter за Go от Go екипа в Google. Показва стилистични грешки. 152 | 153 | go get -u github.com/golang/lint/golint 154 | 155 | И го викаме с : (drum rolls) 156 | 157 | golint . 158 | 159 | или 160 | 161 | golint ./... 162 | 163 | * Други linter-и 164 | 165 | [[https://github.com/alecthomas/gometalinter][gometalinter]] обединява и унифицира много различни вградени и външни инструменти за анализ на кода 166 | 167 | go get -u github.com/alecthomas/gometalinter 168 | gometalinter --install --update 169 | 170 | Включва: 171 | 172 | - Вече споменатите `go`fmt` , `go`vet` , `golint` и `goimports` 173 | - [[https://golang.org/x/tools/cmd/gotype][gotype]] - Синтактичен и семантичен анализ, подобен на Go компилатора 174 | - [[https://github.com/tsenart/deadcode][deadcode]] - Намира неизползван код 175 | - [[https://github.com/alecthomas/gocyclo][gocyclo]] - Изчислява [[https://en.wikipedia.org/wiki/Cyclomatic_complexity][цикломатичната сложност]] на функциите 176 | - [[https://github.com/opennota/check][varcheck]] - Намира неизползвани глобални променливи и константи 177 | - [[https://github.com/opennota/check][structcheck]] - Намира неизползвани полета на структури 178 | - [[https://github.com/opennota/check][aligncheck]] - Предпазва от неоптимално align-нати структури 179 | 180 | * ... 181 | 182 | - [[https://github.com/kisielk/errcheck][errcheck]] - Подсигурява, че error return стойностите се проверяват 183 | - [[https://github.com/mibk/dupl][dupl]] - Проверява за повтарящ се код 184 | - [[https://github.com/gordonklaus/ineffassign/blob/master/list][ineffassign]] - Проверява когато присвоявания към съществуващи променливи не се използват 185 | - [[https://github.com/mvdan/interfacer][interfacer]] - Предлага използването на по-малки интерфейси 186 | - [[https://github.com/mdempsky/unconvert][unconvert]] - Показва ненужни type conversions 187 | - [[https://github.com/jgautheron/goconst][goconst]] - Намира повтарящи се string-ове, които да станат константи 188 | - [[https://github.com/dominikh/go-simple][gosimple]] - Предлага начини за опростяване на кода 189 | - [[https://github.com/dominikh/go-staticcheck][staticcheck]] - Предпазва от грешки при извикването на някои чести функции 190 | 191 | \... и други! Прекалено тежко е да се се пуска всичко на всеки filesave, за разлика от `gofmt`, `goimports` или `golint`. 192 | 193 | Безценен е обаче като метрика и част от CI процес. 194 | 195 | * Online tools 196 | 197 | .link https://godoc.org/github.com/docker/libnetwork 198 | 199 | .link https://goreportcard.com/report/github.com/docker/libnetwork 200 | 201 | * gorename 202 | 203 | Позволява прецизно type-safe преименуване на неща в кода 204 | 205 | go get -u golang.org/x/tools/cmd/gorename 206 | 207 | С даден файл 208 | 209 | gorename -from filename.go::NameOfSomething -to NewNameOfSomething 210 | 211 | само разпознава какво е Something и го преименува в правилните файлове. 212 | 213 | Или 214 | 215 | gorename -from '"site.com/package/path".structName[.MethodName]' -to NewNameOfSomething 216 | 217 | Преименува дадения идентификатор по даден пакет и идентификатор. 218 | 219 | Навярно няма да го ползвате ръчно. 220 | 221 | * Guru 222 | 223 | go get -u golang.org/x/tools/cmd/guru 224 | 225 | Вие питате, а той ви отговаря на въпроси относно кода ви. 226 | 227 | - implements 228 | - describe 229 | - callees 230 | - callers 231 | 232 | Не се ползва ръчно - или поне не лесно. 233 | 234 | [[http://golang.org/s/using-guru]] 235 | 236 | * Интеграция с редактори 237 | 238 | Повечето tool-ве описане до тук са писани с идеята, че ще бъдат интегрирани или поне с възможността да бъдат интегрирани в един или друг текстов редактор и цялостно IDE. 239 | 240 | - gocode и guru са добри примери за това. 241 | - между другото gocode е демон (тоест върви фоново) за context aware autocompletion. 242 | 243 | Общото между двата и причината да не ви показваме как се ползват ръчно е, че и двата работят с byte offset, а не с номера на редове. 244 | 245 | 246 | * build tags (restrictions) 247 | 248 | В go има едно нещо наречено build tag-ове или build restrictions 249 | 250 | Представлят подобен коментар някъде в началото на файла (още преди package): 251 | 252 | // +build tagname 253 | 254 | Сигнализират на компилатора при какви обстоятелства да компилира или да не компилира даден файл 255 | 256 | // +build linux 257 | // +build !386 258 | 259 | казва само под linux, но не под 386 260 | 261 | Може да подаваме наши тагове с: 262 | 263 | go build -tag tagname 264 | 265 | * go generate 266 | 267 | Mожете да укажeте, че искате дадена друга команда да бъде изпълнена върху кода. Use cases: 268 | 269 | - [[https://github.com/gobuffalo/packr][packr]] - ембедване на бинарни файлове в go код :) 270 | - [[https://github.com/clipperhouse/gen][gen]] - code generator, с който може да се постигне нещо като generics 271 | - [[https://golang.org/cmd/yacc/][go yacc]] - yet another compiler compiler (Go edition) 272 | - [[https://godoc.org/golang.org/x/tools/cmd/stringer][stringer]] - автоматично генерира pretty-print `String()` методи на константи 273 | - много други, както възможност и вие сами да си напишете 274 | 275 | Казвате какво и как искате да се пусне, като в началото на някой .go файл слагате: 276 | 277 | //go:generate command args... 278 | 279 | И след това извиквате 280 | 281 | go generate ./... 282 | 283 | * go generate мъдрост 284 | 285 | - Статията [[https://blog.golang.org/generate][Generating Code]] в Golang блога е интересна 286 | 287 | * Command-line аргументи 288 | 289 | - Подадените от терминал аргументи се намират в `os.Args` 290 | - Но Обикновенно искаме да имаме флагове - `flag` ту дъ рескю 291 | 292 | import "flag" 293 | 294 | var stringVar string 295 | 296 | func init() { 297 | flag.StringVar(&stringVar, "paramName", "help message") 298 | } 299 | 300 | func main() { 301 | flag.Parse() 302 | // do something with stringVar 303 | } 304 | 305 | * pprof 306 | 307 | В Go има вграден profiler, който се използва така: 308 | 309 | import _ "net/http/pprof" 310 | 311 | И ако нямате HTTP сървър в програмата, трябва да дoбавите поне: 312 | 313 | go func() { 314 | http.ListenAndServe("localhost:6060", nil) 315 | }() 316 | 317 | След това може да отидете на адреса и да разглеждате информация за heap-а, CPU usage, execution traces, горутини и др. 318 | 319 | Може да се използва и вградения command-line tool: 320 | 321 | go tool pprof http://localhost:6060/debug/pprof/heap 322 | 323 | Повече информация има [[https://blog.golang.org/profiling-go-programs][тук]] и [[https://golang.org/pkg/net/http/pprof/][тук]] 324 | -------------------------------------------------------------------------------- /10-cgo-build.slide: -------------------------------------------------------------------------------- 1 | Cgo и build дълбини 2 | 12.12.2018 3 | 4 | fmi@golang.bg 5 | http://fmi.golang.bg/ 6 | @fmi_golang 7 | 8 | * Но преди това 9 | 10 | - Изберете си проект 11 | - През януари ще има първоначална мини-защита на проектите (по желание) 12 | - Ще напишем в новина какво точно очакваме 13 | 14 | * Въпроси за мъфини 15 | 16 | 17 | * Въпрос #1 18 | 19 | Какво прави `go`generate`./...`? Как? 20 | 21 | - Изпълнява команди, зададени в кода 22 | - Чете коментари от типа `//go:generate`command`--args` 23 | 24 | * Въпрос #2 25 | 26 | Къде можете да видите документацията на `gitlab.com/ivan/libawesome`? 27 | 28 | - На [[https://godoc.org/gitlab.com/ivan/libawesome]] 29 | 30 | * Въпрос #3 31 | 32 | Кога работи вградения race detector? Как се ползва? 33 | 34 | - Само когато изпълнението премине през място, в което има проблем 35 | - `go`test`-race` 36 | - `go`run`-race` 37 | - `go`build`-race` 38 | - `go`install`-race` 39 | 40 | * Въпрос #4 41 | 42 | Как мога да направя файл в пакет, който се build-ва само в Линукс? 43 | 44 | - С коментар `//`+build`linux` в началото му 45 | - Да наименувам файла `*_linux.go` 46 | 47 | * Въпрос #5 48 | 49 | 50 | 51 | * Днес ще видите 52 | 53 | - cgo 54 | - unsafe 55 | - GODEBUG 56 | 57 | * C? Go? Cgo! 58 | 59 | * C в .go файлове 60 | 61 | - Споменавали сме ви, че можете да използвате C библиотеки в go 62 | - Бърз пример как се прави: 63 | 64 | .code code/cgo-runtime/cgo.go 65 | 66 | - Няма пакет "C" 67 | 68 | * Особености 69 | 70 | - Коментарите трябва да се точно преди импорта на "C" 71 | - Те стават header на компилирания С сорс 72 | - Коментари, започващи с `#cgo` са "специални" 73 | 74 | // #cgo CFLAGS: -DPNG_DEBUG=1 75 | // #cgo amd64 386 CFLAGS: -DX86=1 76 | // #cgo LDFLAGS: -lpng 77 | // #include 78 | import "C" 79 | 80 | - CPPFLAGS and LDFLAGS могат да бъдат "наследявани" от други пакети: 81 | 82 | // #cgo pkg-config: png cairo 83 | 84 | 85 | * Go в .c файлове (1) 86 | 87 | - А вече може да използвате Go библиотеки в C 88 | 89 | .code code/cgo-runtime/goc/goc.go 90 | 91 | - Важното: //export 92 | 93 | * Go в .c файлове (2) 94 | 95 | go build -buildmode=c-archive -o goc.a goc.go 96 | 97 | - Ще се генерират `goc.a` и `goc.h` файлове 98 | - В `goc.h`, освен малко boilerplate, ще има и: 99 | 100 | extern void GreetFromGo(GoString p0); 101 | 102 | - `GoString` е част от споменатия boilerplate 103 | 104 | * Go в .c файлове (3) 105 | 106 | - Сега можем да го използваме 107 | 108 | .code code/cgo-runtime/goc/goc.c 109 | 110 | - След това събираме всичко със 111 | 112 | gcc -pthread -o out goc.c goc.a 113 | 114 | * Никога не е толкова просто 115 | .link https://github.com/golang/go/wiki/cgo 116 | .link https://golang.org/src/cmd/cgo/doc.go 117 | 118 | Стигне ли се до компилиране на C, забравете за лесно: 119 | 120 | - Статично линкване 121 | - Cross компилиране 122 | 123 | Но, за това пък, има много от: 124 | 125 | - Wrapper-и във wrapper-и 126 | - Ръчно управление на памет 127 | - Увещаване на linker-а да ви събере всичките частички 128 | 129 | * Ситният шрифт (2) 130 | 131 | Споделяне на памет, алокирана от Go е възможно ако: 132 | 133 | - споделената памет не съдържа указатели към друга алокирана от Go памет 134 | - C не задържи указателя към паметта, след като върне 135 | 136 | runtime проверява за това и ако види нарушение crash-ва програмата. 137 | 138 | 139 | * Още от магиите на "C" 140 | 141 | - В .go файловете, полетата на структури са достъпни с _. Ако в структурата x има поле foo, то е достъпно с x._foo 142 | - Стандартните типове са достъпни през C, с леки промени. `unsigned`int` -> `C.uint` 143 | - Директният достъп до struct, union или enum също е особен: `C.struct_foo` 144 | - В този ред на мисли go не съпортва union, а се представят като масиви от байтове 145 | - И докато сме на темата с ограниченията, не можете да вкарвате поле със C тип в go struct 146 | 147 | 148 | * Error handling 149 | 150 | - Няма нужда да се отказвате от адекватния error handling 151 | - Всяка C функция може да се извиква с две стойности от ляво. Втората очевидно е `errno` 152 | 153 | 154 | n, err := C.sqrt(-1) 155 | 156 | 157 | * Function pointers 158 | 159 | - Лошата новина: не може да викате функция по указател 160 | - Добрата новина: можете да я пазите в променлива 161 | 162 | * Мoля? 163 | 164 | .code code/cgo-runtime/func_point.go 165 | 166 | 167 | * Друг начин за викане на Go от C 168 | 169 | package main 170 | 171 | func Add(a, b int) int { 172 | return a + b 173 | } 174 | 175 | 176 | * Друг начин за викане на Go от C (2) 177 | 178 | #include 179 | 180 | extern int go_add(int, int) __asm__ ("example.main.Add"); 181 | 182 | int main() { 183 | int x = go_add(2, 3); 184 | printf("Result: %d\n", x); 185 | } 186 | 187 | 188 | * Друг начин за викане на Go от C (3) 189 | 190 | - Само за `gccgo` 191 | - Спомняте ли си, че има и друг компилатор? 192 | 193 | all: main 194 | 195 | main: foo.o bar.c 196 | gcc foo.o bar.c -o main 197 | 198 | foo.o: foo.go 199 | gccgo -c foo.go -o foo.o -fgo-prefix=example 200 | 201 | clean: 202 | rm -f main *.o 203 | 204 | 205 | * Указатели без граници 206 | 207 | - Ако си играете със C рано или късно ще попаднете на void* 208 | - Навярно ще ви се наложи и да освободите паметта сочена от указател, който ви е бил върнат 209 | - Може даже да ви се наложи да заделите памет и да подадете указател (като void*) на някоя C функция 210 | - void* на Go-шки е unsafe.Pointer 211 | 212 | 213 | * package unsafe 214 | 215 | Декларира _невинно_ изглеждащите: 216 | 217 | - `func`Alignof(v`ArbitraryType)`uintptr` 218 | - `func`Offsetof(v`ArbitraryType)`uintptr` 219 | - `func`Sizeof(v`ArbitraryType)`uintptr` 220 | - `type`ArbitraryType`int` 221 | - `type`Pointer`*ArbitraryType` 222 | 223 | реално тези дефиниции съществуват главно за документация, имплементацията е в компилатора. 224 | 225 | * Safety third 226 | 227 | unsafe.Pointer има четири важни харектеристики 228 | 229 | - указател към какъв да е тип може да бъде конвертиран към unsafe.Pointer 230 | - unsafe.Pointer може да бъде конвертиран към указател към какъв да е тип 231 | - uintpr може да бъде конвертиран към unsafe.Pointer 232 | - unsafe.Pointer може да бъде конвертиран към uintptr 233 | 234 | Това е практическо заобикаляне на типовата система в Go. 235 | 236 | * Unsafe пример: 237 | 238 | .play code/cgo-runtime/unsafe.go 239 | 240 | 241 | * Адресна аритметика: 242 | 243 | .play code/cgo-runtime/unsafe_2.go /struct/, 244 | 245 | 246 | * Go in Go 247 | 248 | * С? Защо? 249 | 250 | - Вече готови инструменти в Plan9 251 | - Познавайки тези иструменти, могат да се движат бързо 252 | 253 | * Компилатор 254 | 255 | - Вече изцяло на Go, всичкото С го няма 256 | - Не го правят за да се тупат в гърдите 257 | - Коректно Go се пише по - лесно от коректно С 258 | - ... и се дебъгва по - лесно, дори без дебъгер 259 | - Go прави параленото изпълнение тривиално 260 | 261 | * Runtime 262 | 263 | - До 1.5 са имали нужда от специален компилатор за runtime-а на езика 264 | - Вече го няма. Един компилтор по - малко. 265 | - Само един език. По - лесна интеграция, управление на стека 266 | - Простота 267 | 268 | * Как се махат хиляди редове С? 269 | 270 | - Транслатор от С до (лошо!) Go 271 | - Специално написан за тази задача, далеч от генерален 272 | - Махане на странни С идиоми (*p++) 273 | - Statement by statement принтиране на Go код от С 274 | - Compile, Run, Compare, Repeat 275 | .link https://www.youtube.com/watch?v=cF1zJYkBW4A&feature=youtu.be 276 | 277 | * Също преминали в Go Land: 278 | 279 | - Assembler 280 | - Linker 281 | 282 | * Go assembler 283 | 284 | - Го има собствен assembler език 285 | - ЗАЩО!? 286 | 287 | * Go assembler 288 | 289 | - Повечето assembler езици са почти еднакви 290 | - Не вярвате? 291 | 292 | * IBM System/360 293 | 294 | 1 PRINT NOGEN 295 | 2 STOCK1 START 0 296 | 3 BEGIN BALR 11,0 297 | 4 USING *,11 298 | 5 MVC NEWOH,OLDOH 299 | 6 AP NEWOH,RECPT 300 | 7 AP NEWOH,ISSUE 301 | 8 EOJ 302 | 11 OLDOH DC PL4'9' 303 | 12 RECPT DC PL4'4' 304 | 13 ISSUE DC PL4'6' 305 | 14 NEWOH DS PL4 306 | 15 END BEGIN 307 | 308 | 309 | * Apollo 11 Guidance Computer 310 | 311 | # TO ENTER A JOB REQUEST REQUIRING NO VAC AREA: 312 | 313 | COUNT 02/EXEC 314 | 315 | NOVAC INHINT 316 | AD FAKEPRET # LOC(MPAC +6) - LOC(QPRET) 317 | TS NEWPRIO # PRIORITY OF NEW JOB + NOVAC C(FIXLOC) 318 | 319 | EXTEND 320 | INDEX Q # Q WILL BE UNDISTURBED THROUGHOUT. 321 | DCA 0 # 2CADR OF JOB ENTERED. 322 | DXCH NEWLOC 323 | CAF EXECBANK 324 | XCH FBANK 325 | TS EXECTEM1 326 | TCF NOVAC2 # ENTER EXECUTIVE BANK. 327 | 328 | * Използван за ходене до луната 329 | 330 | .image assets/hamilton-code.jpg 331 | 332 | Маргарет Хамилтън 333 | 334 | * PDP-10 335 | 336 | TITLE COUNT 337 | 338 | A=1 ;Define a name for an accumulator. 339 | 340 | START: MOVSI A,-100 ;initialize loop counter. 341 | ;A contains -100,,0 342 | LOOP: HRRZM A,TABLE(A) ;Use right half of A to index. 343 | AOBJN A,LOOP ;Add 1 to both halves (-77,,1 -76,,2 etc.) 344 | ;Jump if still negative. 345 | .VALUE ;Halt program. 346 | 347 | TABLE: BLOCK 100 ;Assemble space to fill up. 348 | 349 | END START ;End the assembly. 350 | 351 | * PDP-11 352 | 353 | / a3 -- pdp-11 assembler pass 1 354 | 355 | assem: 356 | jsr pc,readop 357 | jsr pc,checkeos 358 | br ealoop 359 | tst ifflg 360 | beq 3f 361 | cmp r4,$200 362 | blos assem 363 | cmpb (r4),$21 /if 364 | bne 2f 365 | inc ifflg 366 | 2: 367 | cmpb (r4),$22 /endif 368 | bne assem 369 | dec ifflg 370 | br assem 371 | 372 | * Motorola 68000 373 | 374 | strtolower public 375 | link a6,#0 ;Set up stack frame 376 | movea 8(a6),a0 ;A0 = src, from stack 377 | movea 12(a6),a1 ;A1 = dst, from stack 378 | loop move.b (a0)+,d0 ;Load D0 from (src) 379 | cmpi #'A',d0 ;If D0 < 'A', 380 | blo copy ;skip 381 | cmpi #'Z',d0 ;If D0 > 'Z', 382 | bhi copy ;skip 383 | addi #'a'-'A',d0 ;D0 = lowercase(D0) 384 | copy move.b d0,(a1)+ ;Store D0 to (dst) 385 | bne loop ;Repeat while D0 <> NUL 386 | unlk a6 ;Restore stack frame 387 | rts ;Return 388 | end 389 | 390 | * CRAY-1 391 | 392 | ident slice 393 | V6 0 ; initialize S 394 | A4 S0 ; initialize *x 395 | A5 S1 ; initialize *y 396 | A3 S2 ; initialize i 397 | loop S0 A3 398 | JSZ exit ; if S0 == 0 goto exit 399 | VL A3 ; set vector length 400 | V11 ,A4,1 ; load slice of x[i], stride 1 401 | V12 ,A5,1 ; load slice of y[i], stride 1 402 | V13 V11 *F V12 ; slice of x[i] * y[i] 403 | V6 V6 +F V13 ; partial sum 404 | A14 VL ; get vector length of this iteration 405 | A4 A4 + A14 ; *x = *x + VL 406 | A5 A5 + A14 ; *y = *y + VL 407 | A3 A3 - A14 ; i = i - VL 408 | J loop 409 | exit 410 | 411 | * Go assembler 412 | 413 | Имат доста обща структура: 414 | 415 | subroutine header 416 | label: 417 | instruction operand... ; comment 418 | ... 419 | 420 | - Защо да не се използва това? 421 | 422 | * Нека ги направим еднакви! 423 | 424 | Чрез собствен assembler, който след това лесно се превежда до реалния на всяка машина. 425 | 426 | * Един пример 427 | 428 | Нека разгледаме go програмата 429 | 430 | package add 431 | 432 | func add(a, b int) int { 433 | return a + b 434 | } 435 | 436 | И сега генерирания assembly 437 | 438 | * 32-bit x86 (386) 439 | 440 | TEXT add(SB), $0-12 441 | MOVL a+4(FP), BX 442 | ADDL b+8(FP), BX 443 | MOVL BX, 12(FP) 444 | RET 445 | 446 | * 64-bit x86 (amd64) 447 | 448 | TEXT add(SB), $0-24 449 | MOVQ b+16(FP), AX 450 | MOVQ a+8(FP), CX 451 | ADDQ CX, AX 452 | MOVQ AX, 24(FP) 453 | RET 454 | 455 | * 64-bit arm (arm64) 456 | 457 | TEXT add(SB), $-8-24 458 | MOVD a(FP), R0 459 | MOVD b+8(FP), R1 460 | ADD R1, R0 461 | MOVD R0, 16(FP) 462 | RET 463 | 464 | * S390 (s390x) 465 | 466 | TEXT add(SB), $0-24 467 | MOVD a(FP), R1 468 | MOVD b+8(FP), R2 469 | ADD R2, R1, R1 470 | MOVD R1, 16(FP) 471 | RET 472 | 473 | * 64-bit Power (ppc64le) 474 | 475 | TEXT add(SB), $0-24 476 | MOVD a(FP), R2 477 | MOVD b+8(FP), R3 478 | ADD R3, R2 479 | MOVD R2, 16(FP) 480 | RET 481 | 482 | * ... and so on 483 | 484 | На практика са еднакви. 485 | 486 | * Какво се печели? 487 | 488 | - Нови архитектури се добавят изключително лесно 489 | - Прави се само таблица с инстрункциите `real`->`go`assembly` 490 | - Даже програми, които четата PDF с документация на процесор и генерира такава 491 | 492 | * Пример? 493 | 494 | .link https://github.com/golang/go/blob/master/src/cmd/internal/obj/arm64/asm7.go 495 | 496 | * Повече от командир Pike 497 | 498 | .link https://www.youtube.com/watch?v=KINIAgRpkDA 499 | 500 | * Go linker 501 | 502 | - Също написан на Go. Вече доста бърз. 503 | 504 | * GODEBUG 505 | 506 | * Добре, компилира се! 507 | 508 | - Вече програмата работи 509 | - ... почти 510 | - ... прави неща, които не разбирам 511 | - Какво!? Сигурен съм, че имах повече от 100 MB свободна рам! 512 | 513 | * Повече информация 514 | 515 | - Можете да получите повече информация за работеща програма с флагове на GODEBUG променливата на средата: 516 | 517 | export GODEBUG="name=flag" 518 | 519 | Пример: 520 | 521 | export GODEBUG="gctrace=2,invalidptr=1" 522 | 523 | Позволява: 524 | 525 | - Проследяваме на всяка алокация и освобаждаване на памет (allocfreetrace) 526 | - Спрете конкурентния GC. Всичко става stop-the-world (gcstoptheworld) 527 | - Информация за всяко пускане на GC (gctrace) 528 | 529 | * ... 530 | 531 | - Показване на състоянието на scheduler-a (scheddetail и schedtrace) 532 | - Спрете намаляването на goroutine стековете (gcshrinkstackoff) 533 | - Всяка алокация да бъде на нова страница и да не се преизползват адреси (efence) 534 | - Много други 535 | 536 | * GOGC 537 | 538 | - Променлива на средата, подобна на GODEBUG 539 | - Определя колко често да се пуска garbage collector-а 540 | - Стойноста е колко процента трябва да стане ново алокираното пространство в heap-a спрямо "живия" heap. След стигането на тази стойност се пуска чистенето на боклук. 541 | - По подразбиране `GOGC=100` 542 | - Възможни са всякакви числа, 200, 300 543 | - `GOGC=off` спира събирането на боклук изцяло 544 | 545 | * GOMAXPROCS 546 | 547 | - Променлива на средата 548 | - Функция `GOMAXPROCS(n`int)` от пакета `runtime` 549 | - Определя на колко истински нишки от операционната система ще се изпълнява *вашия* Go код 550 | -------------------------------------------------------------------------------- /11-dependency-management.slide: -------------------------------------------------------------------------------- 1 | Управление на зависимости 2 | 19.12.2018 3 | 4 | fmi@golang.bg 5 | http://fmi.golang.bg/ 6 | @fmi_golang 7 | 8 | * Но преди това 9 | 10 | - Разглеждаме проектите ви, съжаляваме че сме бавни 11 | - Следващото домашно е зад ъгъла, честно 12 | 13 | * Въпроси за мъфини 14 | 15 | * Върпос #1 16 | 17 | * Върпос #2 18 | 19 | * Върпос #3 20 | 21 | * Върпос #4 22 | 23 | * Днес 24 | 25 | - Работа със зависимости в Go 26 | - Ще напишем кратък проект в час 27 | 28 | * Зависимости? 29 | 30 | Аз съм независим човек, не ми трябват! 31 | 32 | * Зависимости 33 | 34 | На кратко това е: 35 | 36 | - Код писан от други хора, който искате да използвате 37 | 38 | * Това е лесно! Нали? 39 | 40 | - Да, "просто" използвам `go`get` и гото, нали? 41 | - Често това е достатъчно 42 | - Но още по - често не е 43 | 44 | * Проблемите 45 | 46 | - Наличност във времето 47 | - Повторими build-ове 48 | 49 | * Живота в свят след `leftpad` 50 | 51 | - Какво става ако някоя ваша зависимост изчезне? 52 | - Ами ако е нещо, от което зависят милиони хора? 53 | - Контекст: [[https://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/][статия от времето на leftpad]] 54 | - Изхвърляте кода си и започвате на ново? 55 | - Има решение, за него по - късно. 56 | 57 | * Повторими строежи 58 | 59 | Задача! 60 | 61 | Ако Иво и Дойчо build-нат commit на go проект, един и същи ли ще е резултата? 62 | 63 | За нещастие - не. 64 | 65 | * Неповторими build-ове? 66 | 67 | Къде е проблемът? 68 | 69 | - Някоя зависимост може да се е променила и `go`get` да вземе нова версия 70 | - Стар код в `$GOPATH` 71 | - При наличието на десетки зависимости лесно става много трудно 72 | 73 | * Защо ни е това? 74 | 75 | - Сигурност 76 | - Quality Assurance 77 | 78 | * О, това изглежда важно! 79 | 80 | Значи има решение още от самото начало на езика, нали? 81 | 82 | Само да можех да опиша кои версии точно искам! 83 | 84 | Как се прави това? Нещо с `go` tool-а, нали? 85 | 86 | * 😱 87 | 88 | История 89 | 90 | - В първите версии на Go нямаше нищо 91 | - Авторите вярваха, че всички go developer-и ще могат да поддържат стабилни и backward compatible интерфейси, подобно на стандартната библиотека 92 | - Реалният свят показа, че предположението не е вярно 93 | - `Go1.5` добави The Vendor Experiment, от 1.6 е винаги включен 94 | - Това позволява да си запазите вашите зависимости в хранилището, заедно с вашия код... 95 | - ... като ръчно си копирате всички файлове там 96 | 97 | * Иструментите 98 | 99 | Това породи цяла зоогическа градина от иструменти, които го правят. Някои от тях: 100 | 101 | glide, godep, govendor, vendor, godm, vendetta, gsv, gom, govend и много други 102 | 103 | * Как работи това? 104 | 105 | - Имате `vendor/` директория 106 | - Когато види `gitlab.com/package/second` build tool-a ще потърси първо в `vendor/gitlab.com/package/second` 107 | - Копирате си файловете от хранилището в тази директория 108 | - Magic! 109 | 110 | * dep 111 | 112 | Известно време се използваше зоогическата градина от инструменти. 113 | 114 | От Go екипа преди около 3 години решиха да направят официален "експеримент" - `dep`. 115 | 116 | * daily dep 117 | 118 | - Стъпва на [[https://semver.org][semver]] 119 | - Хранилищата си слагат тагове `vX.Y.Z` 120 | - В `Gopkg.toml` фай описвате какви версии искате 121 | - `dep`ensure` ще попълни `vendor` директорията 122 | - `dep`ensure`-add`` за добавяне на нови пакети 123 | 124 | * `Gopkg.toml` и `Gopkg.lock` 125 | 126 | Първият държи какво желаете 127 | 128 | Вторият държи кои точно версии са били намерени 129 | 130 | * `Gopkg.toml` 131 | 132 | [[constraint]] 133 | name = "github.com/gorilla/mux" 134 | version = "1.6.2" 135 | 136 | [[constraint]] 137 | name = "github.com/howeyc/fsnotify" 138 | version = "0.9.0" 139 | 140 | [[constraint]] 141 | branch = "master" 142 | name = "github.com/ironsmile/sql-migrate" 143 | 144 | * go modules 145 | 146 | - В go1.11 вече имаме офицалия вариант 147 | - ... който няма нищо общо с `dep` 148 | - Експеримента все пак беше полезен 149 | - Все още е зад флаг 150 | 151 | * go modules (2) 152 | 153 | - Част от `go` tool-a: `go mod` 154 | 155 | * go modules (3) 156 | 157 | go mod init 158 | 159 | - Създава `go.mod` файл със специален синтаксис 160 | - Върши същото като Gopkg.toml 161 | - След това `go`get` добавя в този файл 162 | - `go`build` знае как да ги намери 163 | - `go`mod`vendor` ги добавя в `vendor` директорията 164 | 165 | 166 | * Demo 167 | -------------------------------------------------------------------------------- /12-faq-and-pprof.slide: -------------------------------------------------------------------------------- 1 | Q&A с привкус на pprof 2 | 09.01.2019 3 | 4 | fmi@golang.bg 5 | http://fmi.golang.bg/ 6 | @fmi_golang 7 | 8 | * Първа "защита" на проектите 9 | 10 | Ще бъде на 16.01, следващата седмица. 11 | Има новина на сайта, която обяснява какво очакваме. На кратко: 12 | 13 | - Отворено хранилище 14 | - Отворен лиценз 15 | - README 16 | - Скелет на проекта 17 | - Някой друг тест 18 | - Пример-предположение ако е библиотека 19 | 20 | * Второ контролно 21 | 22 | Няколко човека питаха за него вече. 23 | Ще е по време на сесията, точно преди да ви напишем оценките. 24 | 25 | * В&О 26 | 27 | - Има ли нещо, което не сме обяснили добре? 28 | - Други неща, които не сте успели да питате вече? 29 | - Нещо за извънземни? 30 | 31 | * Административни 32 | 33 | Има ли нещо с оценяването, което не е станало ясно? 34 | 35 | * pprof демо 36 | 37 | Мишоさん ще ви преведе през магичния свят на профилирането. 38 | 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 ФМИ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # A Make file, that conforms with the web site's expectations. Running 'make' 2 | # has to create a directory called 'compiled-lectures' that will be linked by 3 | # the site's deploy process. 4 | # 5 | # "Compiling" the Go lectures does not mean much. We just copy the files to their 6 | # new location. 7 | 8 | TEMPDIR := $(shell mktemp -d) 9 | COMPILED = compiled-lectures 10 | 11 | all: 12 | rm -rf $(COMPILED) 13 | cp -a ./* "$(TEMPDIR)/" 14 | mv $(TEMPDIR) $(COMPILED) 15 | 16 | clean: 17 | rm -rf $(COMPILED) 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Лекции към курса "Програмиране с Go" 2 | ====== 3 | 4 | Презентациите се пишат с [present](https://godoc.org/golang.org/x/tools/present) tool-а на Go. 5 | 6 | Всяка презентация представлява един `xx-name_of_the_lecture.slide` файл, а кодът към нея се намира в собствена папка в `code/`. 7 | 8 | Инсталация 9 | ----- 10 | 11 | go get -u golang.org/x/tools/cmd/present 12 | 13 | Употреба 14 | ------ 15 | 16 | От текущата директория изпълнявате: 17 | 18 | present -base="assets" 19 | 20 | http://127.0.0.1:3999/ 21 | 22 | 23 | Упътвания за принос 24 | ------ 25 | 26 | За да допринесете по някакъв начин към тези материали, е необходимо първо да си подкарате локално `present`. След като промените някоя лекция, трябва да я прегледате в браузър, за да се уверите, че промените изглеждат така, както сте очаквали. 27 | Препоръчително е да правите pull request-и в съответен branch, например `08-fix-typos` (за поправка на правописни грешки в презентация 8). 28 | -------------------------------------------------------------------------------- /assets/armed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/armed.jpg -------------------------------------------------------------------------------- /assets/code-coverage-html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/code-coverage-html.png -------------------------------------------------------------------------------- /assets/commandline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/commandline.png -------------------------------------------------------------------------------- /assets/creators.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/creators.jpg -------------------------------------------------------------------------------- /assets/exorcist.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/exorcist.jpg -------------------------------------------------------------------------------- /assets/fanin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/fanin.jpg -------------------------------------------------------------------------------- /assets/go-lectures-summary-qr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/go-lectures-summary-qr.png -------------------------------------------------------------------------------- /assets/go-tool-all-the-things.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/go-tool-all-the-things.jpg -------------------------------------------------------------------------------- /assets/golang-only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/golang-only.png -------------------------------------------------------------------------------- /assets/gopher-02-door-communication.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/gopher-02-door-communication.jpg -------------------------------------------------------------------------------- /assets/gopher-02-door.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/gopher-02-door.jpg -------------------------------------------------------------------------------- /assets/gopher-cute.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/gopher-cute.jpg -------------------------------------------------------------------------------- /assets/gopher.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/gopher.jpg -------------------------------------------------------------------------------- /assets/gopherbw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/gopherbw.png -------------------------------------------------------------------------------- /assets/gophereartrumpet.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/gophereartrumpet.jpg -------------------------------------------------------------------------------- /assets/gopherswrench.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/gopherswrench.jpg -------------------------------------------------------------------------------- /assets/hamilton-code.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/hamilton-code.jpg -------------------------------------------------------------------------------- /assets/hoare.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/hoare.jpg -------------------------------------------------------------------------------- /assets/interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/interface.png -------------------------------------------------------------------------------- /assets/interface0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/interface0.png -------------------------------------------------------------------------------- /assets/java-vs-cpp-vs-golang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/java-vs-cpp-vs-golang.png -------------------------------------------------------------------------------- /assets/java-vs-golang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/java-vs-golang.png -------------------------------------------------------------------------------- /assets/panic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/panic.jpg -------------------------------------------------------------------------------- /assets/slice.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/slice.jpg -------------------------------------------------------------------------------- /assets/stack-overflow-go-c-cpp-java.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/stack-overflow-go-c-cpp-java.png -------------------------------------------------------------------------------- /assets/stack-overflow-go-only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/stack-overflow-go-only.png -------------------------------------------------------------------------------- /assets/static/article.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: Helvetica, Arial, sans-serif; 4 | font-size: 16px; 5 | } 6 | pre, 7 | code { 8 | font-family: Menlo, monospace; 9 | font-size: 14px; 10 | } 11 | pre { 12 | line-height: 18px; 13 | margin: 0; 14 | padding: 0; 15 | } 16 | a { 17 | color: #375EAB; 18 | text-decoration: none; 19 | } 20 | a:hover { 21 | text-decoration: underline; 22 | } 23 | p, ul, ol { 24 | margin: 20px; 25 | } 26 | 27 | h1, h2, h3, h4 { 28 | margin: 20px 0; 29 | padding: 0; 30 | color: #375EAB; 31 | font-weight: bold; 32 | } 33 | h1 { 34 | font-size: 24px; 35 | } 36 | h2 { 37 | font-size: 20px; 38 | background: #E0EBF5; 39 | padding: 2px 5px; 40 | } 41 | h3 { 42 | font-size: 20px; 43 | } 44 | h3, h4 { 45 | margin: 20px 5px; 46 | } 47 | h4 { 48 | font-size: 16px; 49 | } 50 | 51 | div#heading { 52 | float: left; 53 | margin: 0 0 10px 0; 54 | padding: 21px 0; 55 | font-size: 20px; 56 | font-weight: normal; 57 | } 58 | 59 | div#topbar { 60 | background: #E0EBF5; 61 | height: 64px; 62 | overflow: hidden; 63 | } 64 | 65 | body { 66 | text-align: center; 67 | } 68 | div#page { 69 | width: 100%; 70 | } 71 | div#page > .container, 72 | div#topbar > .container { 73 | text-align: left; 74 | margin-left: auto; 75 | margin-right: auto; 76 | padding: 0 20px; 77 | width: 900px; 78 | } 79 | div#page.wide > .container, 80 | div#topbar.wide > .container { 81 | width: auto; 82 | } 83 | 84 | div#footer { 85 | text-align: center; 86 | color: #666; 87 | font-size: 14px; 88 | margin: 40px 0; 89 | } 90 | 91 | .author p { 92 | margin: 20, 0, 0, 0px; 93 | } 94 | 95 | div.code, 96 | div.output { 97 | margin: 20px; 98 | padding: 10px; 99 | -webkit-border-radius: 5px; 100 | -moz-border-radius: 5px; 101 | border-radius: 5px; 102 | } 103 | 104 | div.code { background: #e9e9e9; } 105 | div.output { background: black; } 106 | div.output .stdout { color: #e6e6e6; } 107 | div.output .stderr { color: rgb(244, 74, 63); } 108 | div.output .system { color: rgb(255, 209, 77) } 109 | 110 | .buttons { 111 | margin-left: 20px; 112 | } 113 | div.output .buttons { 114 | margin-left: 0; 115 | margin-bottom: 10px; 116 | } 117 | 118 | #toc { 119 | float: right; 120 | margin: 0px 10px; 121 | padding: 10px; 122 | border: 1px solid #e5ecf9; 123 | background-color: white; 124 | max-width: 33%; 125 | 126 | -webkit-border-radius: 5px; 127 | -moz-border-radius: 5px; 128 | border-radius: 5px; 129 | } 130 | 131 | #toc ul, #toc a { 132 | list-style-type: none; 133 | padding-left: 10px; 134 | color: black; 135 | margin: 0px; 136 | } 137 | -------------------------------------------------------------------------------- /assets/static/dir.css: -------------------------------------------------------------------------------- 1 | /* copied from $GOROOT/doc/style.css */ 2 | 3 | body { 4 | margin: 0; 5 | font-family: Helvetica, Arial, sans-serif; 6 | font-size: 16px; 7 | } 8 | pre, 9 | code { 10 | font-family: Menlo, monospace; 11 | font-size: 14px; 12 | } 13 | pre { 14 | line-height: 18px; 15 | } 16 | pre .comment { 17 | color: #375EAB; 18 | } 19 | pre .highlight, 20 | pre .highlight-comment, 21 | pre .selection-highlight, 22 | pre .selection-highlight-comment { 23 | background: #FFFF00; 24 | } 25 | pre .selection, 26 | pre .selection-comment { 27 | background: #FF9632; 28 | } 29 | pre .ln { 30 | color: #999; 31 | } 32 | body { 33 | color: #222; 34 | } 35 | a, 36 | .exampleHeading .text { 37 | color: #375EAB; 38 | text-decoration: none; 39 | } 40 | a:hover, 41 | .exampleHeading .text:hover { 42 | text-decoration: underline; 43 | } 44 | p, 45 | pre, 46 | ul, 47 | ol { 48 | margin: 20px; 49 | } 50 | pre { 51 | background: #e9e9e9; 52 | padding: 10px; 53 | 54 | -webkit-border-radius: 5px; 55 | -moz-border-radius: 5px; 56 | border-radius: 5px; 57 | } 58 | 59 | h1, 60 | h2, 61 | h3, 62 | h4, 63 | .rootHeading { 64 | margin: 20px 0; 65 | padding: 0; 66 | color: #375EAB; 67 | font-weight: bold; 68 | } 69 | h1 { 70 | font-size: 24px; 71 | } 72 | h2 { 73 | font-size: 20px; 74 | background: #E0EBF5; 75 | padding: 2px 5px; 76 | } 77 | h3 { 78 | font-size: 20px; 79 | } 80 | h3, 81 | h4 { 82 | margin: 20px 5px; 83 | } 84 | h4 { 85 | font-size: 16px; 86 | } 87 | 88 | dl { 89 | margin: 20px; 90 | } 91 | dd { 92 | margin: 2px 20px; 93 | } 94 | dl, 95 | dd { 96 | font-size: 14px; 97 | } 98 | div#nav table td { 99 | vertical-align: top; 100 | } 101 | 102 | div#heading { 103 | float: left; 104 | margin: 0 0 10px 0; 105 | padding: 21px 0; 106 | font-size: 20px; 107 | font-weight: normal; 108 | } 109 | div#heading a { 110 | color: #222; 111 | text-decoration: none; 112 | } 113 | 114 | div#topbar { 115 | background: #E0EBF5; 116 | height: 64px; 117 | } 118 | 119 | body { 120 | text-align: center; 121 | } 122 | div#page, 123 | div#topbar > .container { 124 | clear: both; 125 | text-align: left; 126 | margin-left: auto; 127 | margin-right: auto; 128 | padding: 0 20px; 129 | width: 900px; 130 | } 131 | div#page.wide, 132 | div#topbar > .wide { 133 | width: auto; 134 | } 135 | div#plusone { 136 | float: right; 137 | } 138 | 139 | div#footer { 140 | color: #666; 141 | font-size: 14px; 142 | margin: 40px 0; 143 | } 144 | 145 | div#menu > a, 146 | div#menu > input { 147 | padding: 10px; 148 | 149 | text-decoration: none; 150 | font-size: 16px; 151 | 152 | -webkit-border-radius: 5px; 153 | -moz-border-radius: 5px; 154 | border-radius: 5px; 155 | } 156 | div#menu > a, 157 | div#menu > input { 158 | border: 1px solid #375EAB; 159 | } 160 | div#menu > a { 161 | color: white; 162 | background: #375EAB; 163 | } 164 | 165 | div#menu { 166 | float: right; 167 | min-width: 590px; 168 | padding: 10px 0; 169 | text-align: right; 170 | } 171 | div#menu > a { 172 | margin-right: 5px; 173 | margin-bottom: 10px; 174 | 175 | padding: 10px; 176 | } 177 | div#menu > input { 178 | position: relative; 179 | top: 1px; 180 | width: 60px; 181 | background: white; 182 | color: #222; 183 | } 184 | div#menu > input.inactive { 185 | color: #999; 186 | } 187 | -------------------------------------------------------------------------------- /assets/static/dir.js: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // copied from $GOROOT/doc/godocs.js 6 | 7 | function bindEvent(el, e, fn) { 8 | if (el.addEventListener){ 9 | el.addEventListener(e, fn, false); 10 | } else if (el.attachEvent){ 11 | el.attachEvent('on'+e, fn); 12 | } 13 | } 14 | 15 | function godocs_bindSearchEvents() { 16 | var search = document.getElementById('search'); 17 | if (!search) { 18 | // no search box (index disabled) 19 | return; 20 | } 21 | function clearInactive() { 22 | if (search.className == "inactive") { 23 | search.value = ""; 24 | search.className = ""; 25 | } 26 | } 27 | function restoreInactive() { 28 | if (search.value !== "") { 29 | return; 30 | } 31 | if (search.type != "search") { 32 | search.value = search.getAttribute("placeholder"); 33 | } 34 | search.className = "inactive"; 35 | } 36 | restoreInactive(); 37 | bindEvent(search, 'focus', clearInactive); 38 | bindEvent(search, 'blur', restoreInactive); 39 | } 40 | 41 | bindEvent(window, 'load', godocs_bindSearchEvents); 42 | -------------------------------------------------------------------------------- /assets/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/assets/static/favicon.ico -------------------------------------------------------------------------------- /assets/static/play.js: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | function initPlayground(transport) { 6 | "use strict"; 7 | 8 | function text(node) { 9 | var s = ""; 10 | for (var i = 0; i < node.childNodes.length; i++) { 11 | var n = node.childNodes[i]; 12 | if (n.nodeType === 1 && n.tagName === "SPAN" && n.className != "number") { 13 | var innerText = n.innerText === undefined ? "textContent" : "innerText"; 14 | s += n[innerText] + "\n"; 15 | continue; 16 | } 17 | if (n.nodeType === 1 && n.tagName !== "BUTTON") { 18 | s += text(n); 19 | } 20 | } 21 | return s; 22 | } 23 | 24 | function init(code) { 25 | var output = document.createElement('div'); 26 | var outpre = document.createElement('pre'); 27 | var running; 28 | 29 | // TODO(adg): check that jquery etc is loaded. 30 | $(output).resizable({ 31 | handles: "n,w,nw", 32 | minHeight: 27, 33 | minWidth: 135, 34 | maxHeight: 608, 35 | maxWidth: 990 36 | }); 37 | 38 | function onKill() { 39 | if (running) running.Kill(); 40 | } 41 | 42 | function onRun(e) { 43 | onKill(); 44 | output.style.display = "block"; 45 | outpre.innerHTML = ""; 46 | run1.style.display = "none"; 47 | var options = {Race: e.shiftKey}; 48 | running = transport.Run(text(code), PlaygroundOutput(outpre), options); 49 | } 50 | 51 | function onClose() { 52 | onKill(); 53 | output.style.display = "none"; 54 | run1.style.display = "inline-block"; 55 | } 56 | 57 | var run1 = document.createElement('button'); 58 | run1.innerHTML = 'Run'; 59 | run1.className = 'run'; 60 | run1.addEventListener("click", onRun, false); 61 | var run2 = document.createElement('button'); 62 | run2.className = 'run'; 63 | run2.innerHTML = 'Run'; 64 | run2.addEventListener("click", onRun, false); 65 | var kill = document.createElement('button'); 66 | kill.className = 'kill'; 67 | kill.innerHTML = 'Kill'; 68 | kill.addEventListener("click", onKill, false); 69 | var close = document.createElement('button'); 70 | close.className = 'close'; 71 | close.innerHTML = 'Close'; 72 | close.addEventListener("click", onClose, false); 73 | 74 | var button = document.createElement('div'); 75 | button.classList.add('buttons'); 76 | button.appendChild(run1); 77 | // Hack to simulate insertAfter 78 | code.parentNode.insertBefore(button, code.nextSibling); 79 | 80 | var buttons = document.createElement('div'); 81 | buttons.classList.add('buttons'); 82 | buttons.appendChild(run2); 83 | buttons.appendChild(kill); 84 | buttons.appendChild(close); 85 | 86 | output.classList.add('output'); 87 | output.appendChild(buttons); 88 | output.appendChild(outpre); 89 | output.style.display = "none"; 90 | code.parentNode.insertBefore(output, button.nextSibling); 91 | } 92 | 93 | var play = document.querySelectorAll('div.playground'); 94 | for (var i = 0; i < play.length; i++) { 95 | init(play[i]); 96 | } 97 | } 98 | 99 | -------------------------------------------------------------------------------- /assets/static/playground.js: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | In the absence of any formal way to specify interfaces in JavaScript, 7 | here's a skeleton implementation of a playground transport. 8 | 9 | function Transport() { 10 | // Set up any transport state (eg, make a websocket connnection). 11 | return { 12 | Run: function(body, output, options) { 13 | // Compile and run the program 'body' with 'options'. 14 | // Call the 'output' callback to display program output. 15 | return { 16 | Kill: function() { 17 | // Kill the running program. 18 | } 19 | }; 20 | } 21 | }; 22 | } 23 | 24 | // The output callback is called multiple times, and each time it is 25 | // passed an object of this form. 26 | var write = { 27 | Kind: 'string', // 'start', 'stdout', 'stderr', 'end' 28 | Body: 'string' // content of write or end status message 29 | } 30 | 31 | // The first call must be of Kind 'start' with no body. 32 | // Subsequent calls may be of Kind 'stdout' or 'stderr' 33 | // and must have a non-null Body string. 34 | // The final call should be of Kind 'end' with an optional 35 | // Body string, signifying a failure ("killed", for example). 36 | 37 | // The output callback must be of this form. 38 | // See PlaygroundOutput (below) for an implementation. 39 | function outputCallback(write) { 40 | // Append writes to 41 | } 42 | */ 43 | 44 | function HTTPTransport() { 45 | 'use strict'; 46 | 47 | // TODO(adg): support stderr 48 | 49 | function playback(output, events) { 50 | var timeout; 51 | output({Kind: 'start'}); 52 | function next() { 53 | if (events.length === 0) { 54 | output({Kind: 'end'}); 55 | return; 56 | } 57 | var e = events.shift(); 58 | if (e.Delay === 0) { 59 | output({Kind: 'stdout', Body: e.Message}); 60 | next(); 61 | return; 62 | } 63 | timeout = setTimeout(function() { 64 | output({Kind: 'stdout', Body: e.Message}); 65 | next(); 66 | }, e.Delay / 1000000); 67 | } 68 | next(); 69 | return { 70 | Stop: function() { 71 | clearTimeout(timeout); 72 | } 73 | } 74 | } 75 | 76 | function error(output, msg) { 77 | output({Kind: 'start'}); 78 | output({Kind: 'stderr', Body: msg}); 79 | output({Kind: 'end'}); 80 | } 81 | 82 | var seq = 0; 83 | return { 84 | Run: function(body, output, options) { 85 | seq++; 86 | var cur = seq; 87 | var playing; 88 | $.ajax('/compile', { 89 | type: 'POST', 90 | data: {'version': 2, 'body': body}, 91 | dataType: 'json', 92 | success: function(data) { 93 | if (seq != cur) return; 94 | if (!data) return; 95 | if (playing != null) playing.Stop(); 96 | if (data.Errors) { 97 | error(output, data.Errors); 98 | return; 99 | } 100 | playing = playback(output, data.Events); 101 | }, 102 | error: function() { 103 | error(output, 'Error communicating with remote server.'); 104 | } 105 | }); 106 | return { 107 | Kill: function() { 108 | if (playing != null) playing.Stop(); 109 | output({Kind: 'end', Body: 'killed'}); 110 | } 111 | }; 112 | } 113 | }; 114 | } 115 | 116 | function SocketTransport() { 117 | 'use strict'; 118 | 119 | var id = 0; 120 | var outputs = {}; 121 | var started = {}; 122 | var websocket = new WebSocket('ws://' + window.location.host + '/socket'); 123 | 124 | websocket.onclose = function() { 125 | console.log('websocket connection closed'); 126 | } 127 | 128 | websocket.onmessage = function(e) { 129 | var m = JSON.parse(e.data); 130 | var output = outputs[m.Id]; 131 | if (output === null) 132 | return; 133 | if (!started[m.Id]) { 134 | output({Kind: 'start'}); 135 | started[m.Id] = true; 136 | } 137 | output({Kind: m.Kind, Body: m.Body}); 138 | } 139 | 140 | function send(m) { 141 | websocket.send(JSON.stringify(m)); 142 | } 143 | 144 | return { 145 | Run: function(body, output, options) { 146 | var thisID = id+''; 147 | id++; 148 | outputs[thisID] = output; 149 | send({Id: thisID, Kind: 'run', Body: body, Options: options}); 150 | return { 151 | Kill: function() { 152 | send({Id: thisID, Kind: 'kill'}); 153 | } 154 | }; 155 | } 156 | }; 157 | } 158 | 159 | function PlaygroundOutput(el) { 160 | 'use strict'; 161 | 162 | return function(write) { 163 | if (write.Kind == 'start') { 164 | el.innerHTML = ''; 165 | return; 166 | } 167 | 168 | var cl = 'system'; 169 | if (write.Kind == 'stdout' || write.Kind == 'stderr') 170 | cl = write.Kind; 171 | 172 | var m = write.Body; 173 | if (write.Kind == 'end') 174 | m = '\nProgram exited' + (m?(': '+m):'.'); 175 | 176 | if (m.indexOf('IMAGE:') === 0) { 177 | // TODO(adg): buffer all writes before creating image 178 | var url = 'data:image/png;base64,' + m.substr(6); 179 | var img = document.createElement('img'); 180 | img.src = url; 181 | el.appendChild(img); 182 | return; 183 | } 184 | 185 | // ^L clears the screen. 186 | var s = m.split('\x0c'); 187 | if (s.length > 1) { 188 | el.innerHTML = ''; 189 | m = s.pop(); 190 | } 191 | 192 | m = m.replace(/&/g, '&'); 193 | m = m.replace(//g, '>'); 195 | 196 | var needScroll = (el.scrollTop + el.offsetHeight) == el.scrollHeight; 197 | 198 | var span = document.createElement('span'); 199 | span.className = cl; 200 | span.innerHTML = m; 201 | el.appendChild(span); 202 | 203 | if (needScroll) 204 | el.scrollTop = el.scrollHeight - el.offsetHeight; 205 | } 206 | } 207 | 208 | (function() { 209 | function lineHighlight(error) { 210 | var regex = /prog.go:([0-9]+)/g; 211 | var r = regex.exec(error); 212 | while (r) { 213 | $(".lines div").eq(r[1]-1).addClass("lineerror"); 214 | r = regex.exec(error); 215 | } 216 | } 217 | function highlightOutput(wrappedOutput) { 218 | return function(write) { 219 | if (write.Body) lineHighlight(write.Body); 220 | wrappedOutput(write); 221 | } 222 | } 223 | function lineClear() { 224 | $(".lineerror").removeClass("lineerror"); 225 | } 226 | 227 | // opts is an object with these keys 228 | // codeEl - code editor element 229 | // outputEl - program output element 230 | // runEl - run button element 231 | // fmtEl - fmt button element (optional) 232 | // shareEl - share button element (optional) 233 | // shareURLEl - share URL text input element (optional) 234 | // shareRedirect - base URL to redirect to on share (optional) 235 | // toysEl - toys select element (optional) 236 | // enableHistory - enable using HTML5 history API (optional) 237 | // transport - playground transport to use (default is HTTPTransport) 238 | function playground(opts) { 239 | var code = $(opts.codeEl); 240 | var transport = opts['transport'] || new HTTPTransport(); 241 | var running; 242 | 243 | // autoindent helpers. 244 | function insertTabs(n) { 245 | // find the selection start and end 246 | var start = code[0].selectionStart; 247 | var end = code[0].selectionEnd; 248 | // split the textarea content into two, and insert n tabs 249 | var v = code[0].value; 250 | var u = v.substr(0, start); 251 | for (var i=0; i 0) { 265 | curpos--; 266 | if (el.value[curpos] == "\t") { 267 | tabs++; 268 | } else if (tabs > 0 || el.value[curpos] == "\n") { 269 | break; 270 | } 271 | } 272 | setTimeout(function() { 273 | insertTabs(tabs); 274 | }, 1); 275 | } 276 | 277 | function keyHandler(e) { 278 | if (e.keyCode == 9) { // tab 279 | insertTabs(1); 280 | e.preventDefault(); 281 | return false; 282 | } 283 | if (e.keyCode == 13) { // enter 284 | if (e.shiftKey) { // +shift 285 | run(); 286 | e.preventDefault(); 287 | return false; 288 | } else { 289 | autoindent(e.target); 290 | } 291 | } 292 | return true; 293 | } 294 | code.unbind('keydown').bind('keydown', keyHandler); 295 | var outdiv = $(opts.outputEl).empty(); 296 | var output = $('
').appendTo(outdiv);
297 |   
298 |     function body() {
299 |       return $(opts.codeEl).val();
300 |     }
301 |     function setBody(text) {
302 |       $(opts.codeEl).val(text);
303 |     }
304 |     function origin(href) {
305 |       return (""+href).split("/").slice(0, 3).join("/");
306 |     }
307 |   
308 |     var pushedEmpty = (window.location.pathname == "/");
309 |     function inputChanged() {
310 |       if (pushedEmpty) {
311 |         return;
312 |       }
313 |       pushedEmpty = true;
314 |       $(opts.shareURLEl).hide();
315 |       window.history.pushState(null, "", "/");
316 |     }
317 |     function popState(e) {
318 |       if (e === null) {
319 |         return;
320 |       }
321 |       if (e && e.state && e.state.code) {
322 |         setBody(e.state.code);
323 |       }
324 |     }
325 |     var rewriteHistory = false;
326 |     if (window.history && window.history.pushState && window.addEventListener && opts.enableHistory) {
327 |       rewriteHistory = true;
328 |       code[0].addEventListener('input', inputChanged);
329 |       window.addEventListener('popstate', popState);
330 |     }
331 | 
332 |     function setError(error) {
333 |       if (running) running.Kill();
334 |       lineClear();
335 |       lineHighlight(error);
336 |       output.empty().addClass("error").text(error);
337 |     }
338 |     function loading() {
339 |       lineClear();
340 |       if (running) running.Kill();
341 |       output.removeClass("error").text('Waiting for remote server...');
342 |     }
343 |     function run() {
344 |       loading();
345 |       running = transport.Run(body(), highlightOutput(PlaygroundOutput(output[0])));
346 |     }
347 |     function fmt() {
348 |       loading();
349 |       $.ajax("/fmt", {
350 |         data: {"body": body()},
351 |         type: "POST",
352 |         dataType: "json",
353 |         success: function(data) {
354 |           if (data.Error) {
355 |             setError(data.Error);
356 |           } else {
357 |             setBody(data.Body);
358 |             setError("");
359 |           }
360 |         }
361 |       });
362 |     }
363 | 
364 |     $(opts.runEl).click(run);
365 |     $(opts.fmtEl).click(fmt);
366 |   
367 |     if (opts.shareEl !== null && (opts.shareURLEl !== null || opts.shareRedirect !== null)) {
368 |       var shareURL;
369 |       if (opts.shareURLEl) {
370 |         shareURL = $(opts.shareURLEl).hide();
371 |       }
372 |       var sharing = false;
373 |       $(opts.shareEl).click(function() {
374 |         if (sharing) return;
375 |         sharing = true;
376 |         var sharingData = body();
377 |         $.ajax("/share", {
378 |           processData: false,
379 |           data: sharingData,
380 |           type: "POST",
381 |           complete: function(xhr) {
382 |             sharing = false;
383 |             if (xhr.status != 200) {
384 |               alert("Server error; try again.");
385 |               return;
386 |             }
387 |             if (opts.shareRedirect) {
388 |               window.location = opts.shareRedirect + xhr.responseText;
389 |             }
390 |             if (shareURL) {
391 |               var path = "/p/" + xhr.responseText;
392 |               var url = origin(window.location) + path;
393 |               shareURL.show().val(url).focus().select();
394 |   
395 |               if (rewriteHistory) {
396 |                 var historyData = {"code": sharingData};
397 |                 window.history.pushState(historyData, "", path);
398 |                 pushedEmpty = false;
399 |               }
400 |             }
401 |           }
402 |         });
403 |       });
404 |     }
405 |   
406 |     if (opts.toysEl !== null) {
407 |       $(opts.toysEl).bind('change', function() {
408 |         var toy = $(this).val();
409 |         $.ajax("/doc/play/"+toy, {
410 |           processData: false,
411 |           type: "GET",
412 |           complete: function(xhr) {
413 |             if (xhr.status != 200) {
414 |               alert("Server error; try again.");
415 |               return;
416 |             }
417 |             setBody(xhr.responseText);
418 |           }
419 |         });
420 |       });
421 |     }
422 |   }
423 | 
424 |   window.playground = playground;
425 | })();
426 | 


--------------------------------------------------------------------------------
/assets/static/print.css:
--------------------------------------------------------------------------------
 1 | /* set page layout */
 2 | @page {
 3 |   size: A4 landscape;
 4 | }
 5 | 
 6 | body { 
 7 |   display: block !important;
 8 | }
 9 | 
10 | .slides {
11 |   left: 0;
12 |   top: 0;
13 | }
14 | 
15 | .slides > article {
16 |   position: relative;
17 | 
18 |   left: 0;
19 |   top: 0;
20 | 
21 |   margin: 0 !important;
22 |   page-break-inside: avoid;
23 | 
24 |   text-shadow: none; /* disable shadow */
25 | 
26 |   display: block !important;
27 |   transform: translate(0) !important;
28 |   -o-transform: translate(0) !important;
29 |   -moz-transform: translate(0) !important;
30 |   -webkit-transform: translate3d(0, 0, 0) !important;
31 | }
32 | 
33 | div.code {
34 |   background: rgb(240, 240, 240);
35 | }
36 | 
37 | /* hide click areas */
38 | .slide-area, #prev-slide-area, #next-slide-area {
39 |   display: none;
40 | }
41 | 
42 | /* add explicit links */
43 | a:link:after, a:visited:after {
44 |  content: " (" attr(href) ") ";
45 |  font-size: 50%;
46 | }
47 | 
48 | /* white background */
49 | body {
50 |   background: rgb(255,255,255) !important;
51 | }
52 | 


--------------------------------------------------------------------------------
/assets/static/slides.js:
--------------------------------------------------------------------------------
  1 | // Copyright 2012 The Go Authors. All rights reserved.
  2 | // Use of this source code is governed by a BSD-style
  3 | // license that can be found in the LICENSE file.
  4 | 
  5 | var PERMANENT_URL_PREFIX = '/static/';
  6 | 
  7 | var SLIDE_CLASSES = ['far-past', 'past', 'current', 'next', 'far-next'];
  8 | 
  9 | var PM_TOUCH_SENSITIVITY = 15;
 10 | 
 11 | var curSlide;
 12 | 
 13 | /* ---------------------------------------------------------------------- */
 14 | /* classList polyfill by Eli Grey
 15 |  * (http://purl.eligrey.com/github/classList.js/blob/master/classList.js) */
 16 | 
 17 | if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) {
 18 | 
 19 | (function (view) {
 20 | 
 21 | var
 22 |     classListProp = "classList"
 23 |   , protoProp = "prototype"
 24 |   , elemCtrProto = (view.HTMLElement || view.Element)[protoProp]
 25 |   , objCtr = Object
 26 |     strTrim = String[protoProp].trim || function () {
 27 |     return this.replace(/^\s+|\s+$/g, "");
 28 |   }
 29 |   , arrIndexOf = Array[protoProp].indexOf || function (item) {
 30 |     for (var i = 0, len = this.length; i < len; i++) {
 31 |       if (i in this && this[i] === item) {
 32 |         return i;
 33 |       }
 34 |     }
 35 |     return -1;
 36 |   }
 37 |   // Vendors: please allow content code to instantiate DOMExceptions
 38 |   , DOMEx = function (type, message) {
 39 |     this.name = type;
 40 |     this.code = DOMException[type];
 41 |     this.message = message;
 42 |   }
 43 |   , checkTokenAndGetIndex = function (classList, token) {
 44 |     if (token === "") {
 45 |       throw new DOMEx(
 46 |           "SYNTAX_ERR"
 47 |         , "An invalid or illegal string was specified"
 48 |       );
 49 |     }
 50 |     if (/\s/.test(token)) {
 51 |       throw new DOMEx(
 52 |           "INVALID_CHARACTER_ERR"
 53 |         , "String contains an invalid character"
 54 |       );
 55 |     }
 56 |     return arrIndexOf.call(classList, token);
 57 |   }
 58 |   , ClassList = function (elem) {
 59 |     var
 60 |         trimmedClasses = strTrim.call(elem.className)
 61 |       , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
 62 |     ;
 63 |     for (var i = 0, len = classes.length; i < len; i++) {
 64 |       this.push(classes[i]);
 65 |     }
 66 |     this._updateClassName = function () {
 67 |       elem.className = this.toString();
 68 |     };
 69 |   }
 70 |   , classListProto = ClassList[protoProp] = []
 71 |   , classListGetter = function () {
 72 |     return new ClassList(this);
 73 |   }
 74 | ;
 75 | // Most DOMException implementations don't allow calling DOMException's toString()
 76 | // on non-DOMExceptions. Error's toString() is sufficient here.
 77 | DOMEx[protoProp] = Error[protoProp];
 78 | classListProto.item = function (i) {
 79 |   return this[i] || null;
 80 | };
 81 | classListProto.contains = function (token) {
 82 |   token += "";
 83 |   return checkTokenAndGetIndex(this, token) !== -1;
 84 | };
 85 | classListProto.add = function (token) {
 86 |   token += "";
 87 |   if (checkTokenAndGetIndex(this, token) === -1) {
 88 |     this.push(token);
 89 |     this._updateClassName();
 90 |   }
 91 | };
 92 | classListProto.remove = function (token) {
 93 |   token += "";
 94 |   var index = checkTokenAndGetIndex(this, token);
 95 |   if (index !== -1) {
 96 |     this.splice(index, 1);
 97 |     this._updateClassName();
 98 |   }
 99 | };
100 | classListProto.toggle = function (token) {
101 |   token += "";
102 |   if (checkTokenAndGetIndex(this, token) === -1) {
103 |     this.add(token);
104 |   } else {
105 |     this.remove(token);
106 |   }
107 | };
108 | classListProto.toString = function () {
109 |   return this.join(" ");
110 | };
111 | 
112 | if (objCtr.defineProperty) {
113 |   var classListPropDesc = {
114 |       get: classListGetter
115 |     , enumerable: true
116 |     , configurable: true
117 |   };
118 |   try {
119 |     objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
120 |   } catch (ex) { // IE 8 doesn't support enumerable:true
121 |     if (ex.number === -0x7FF5EC54) {
122 |       classListPropDesc.enumerable = false;
123 |       objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
124 |     }
125 |   }
126 | } else if (objCtr[protoProp].__defineGetter__) {
127 |   elemCtrProto.__defineGetter__(classListProp, classListGetter);
128 | }
129 | 
130 | }(self));
131 | 
132 | }
133 | /* ---------------------------------------------------------------------- */
134 | 
135 | /* Slide movement */
136 | 
137 | function getSlideEl(no) {
138 |   if ((no < 0) || (no >= slideEls.length)) {
139 |     return null;
140 |   } else {
141 |     return slideEls[no];
142 |   }
143 | };
144 | 
145 | function updateSlideClass(slideNo, className) {
146 |   var el = getSlideEl(slideNo);
147 | 
148 |   if (!el) {
149 |     return;
150 |   }
151 | 
152 |   if (className) {
153 |     el.classList.add(className);
154 |   }
155 | 
156 |   for (var i in SLIDE_CLASSES) {
157 |     if (className != SLIDE_CLASSES[i]) {
158 |       el.classList.remove(SLIDE_CLASSES[i]);
159 |     }
160 |   }
161 | };
162 | 
163 | function updateSlides() {
164 |   for (var i = 0; i < slideEls.length; i++) {
165 |     switch (i) {
166 |       case curSlide - 2:
167 |         updateSlideClass(i, 'far-past');
168 |         break;
169 |       case curSlide - 1:
170 |         updateSlideClass(i, 'past');
171 |         break;
172 |       case curSlide:
173 |         updateSlideClass(i, 'current');
174 |         break;
175 |       case curSlide + 1:
176 |         updateSlideClass(i, 'next');
177 |         break;
178 |       case curSlide + 2:
179 |         updateSlideClass(i, 'far-next');
180 |         break;
181 |       default:
182 |         updateSlideClass(i);
183 |         break;
184 |     }
185 |   }
186 | 
187 |   triggerLeaveEvent(curSlide - 1);
188 |   triggerEnterEvent(curSlide);
189 | 
190 |   window.setTimeout(function() {
191 |     // Hide after the slide
192 |     disableSlideFrames(curSlide - 2);
193 |   }, 301);
194 | 
195 |   enableSlideFrames(curSlide - 1);
196 |   enableSlideFrames(curSlide + 2);
197 | 
198 |   updateHash();
199 | };
200 | 
201 | function prevSlide() {
202 |   if (curSlide > 0) {
203 |     curSlide--;
204 | 
205 |     updateSlides();
206 |   }
207 | };
208 | 
209 | function nextSlide() {
210 |   if (curSlide < slideEls.length - 1) {
211 |     curSlide++;
212 | 
213 |     updateSlides();
214 |   }
215 | };
216 | 
217 | /* Slide events */
218 | 
219 | function triggerEnterEvent(no) {
220 |   var el = getSlideEl(no);
221 |   if (!el) {
222 |     return;
223 |   }
224 | 
225 |   var onEnter = el.getAttribute('onslideenter');
226 |   if (onEnter) {
227 |     new Function(onEnter).call(el);
228 |   }
229 | 
230 |   var evt = document.createEvent('Event');
231 |   evt.initEvent('slideenter', true, true);
232 |   evt.slideNumber = no + 1; // Make it readable
233 | 
234 |   el.dispatchEvent(evt);
235 | };
236 | 
237 | function triggerLeaveEvent(no) {
238 |   var el = getSlideEl(no);
239 |   if (!el) {
240 |     return;
241 |   }
242 | 
243 |   var onLeave = el.getAttribute('onslideleave');
244 |   if (onLeave) {
245 |     new Function(onLeave).call(el);
246 |   }
247 | 
248 |   var evt = document.createEvent('Event');
249 |   evt.initEvent('slideleave', true, true);
250 |   evt.slideNumber = no + 1; // Make it readable
251 | 
252 |   el.dispatchEvent(evt);
253 | };
254 | 
255 | /* Touch events */
256 | 
257 | function handleTouchStart(event) {
258 |   if (event.touches.length == 1) {
259 |     touchDX = 0;
260 |     touchDY = 0;
261 | 
262 |     touchStartX = event.touches[0].pageX;
263 |     touchStartY = event.touches[0].pageY;
264 | 
265 |     document.body.addEventListener('touchmove', handleTouchMove, true);
266 |     document.body.addEventListener('touchend', handleTouchEnd, true);
267 |   }
268 | };
269 | 
270 | function handleTouchMove(event) {
271 |   if (event.touches.length > 1) {
272 |     cancelTouch();
273 |   } else {
274 |     touchDX = event.touches[0].pageX - touchStartX;
275 |     touchDY = event.touches[0].pageY - touchStartY;
276 |     event.preventDefault();
277 |   }
278 | };
279 | 
280 | function handleTouchEnd(event) {
281 |   var dx = Math.abs(touchDX);
282 |   var dy = Math.abs(touchDY);
283 | 
284 |   if ((dx > PM_TOUCH_SENSITIVITY) && (dy < (dx * 2 / 3))) {
285 |     if (touchDX > 0) {
286 |       prevSlide();
287 |     } else {
288 |       nextSlide();
289 |     }
290 |   }
291 | 
292 |   cancelTouch();
293 | };
294 | 
295 | function cancelTouch() {
296 |   document.body.removeEventListener('touchmove', handleTouchMove, true);
297 |   document.body.removeEventListener('touchend', handleTouchEnd, true);
298 | };
299 | 
300 | /* Preloading frames */
301 | 
302 | function disableSlideFrames(no) {
303 |   var el = getSlideEl(no);
304 |   if (!el) {
305 |     return;
306 |   }
307 | 
308 |   var frames = el.getElementsByTagName('iframe');
309 |   for (var i = 0, frame; frame = frames[i]; i++) {
310 |     disableFrame(frame);
311 |   }
312 | };
313 | 
314 | function enableSlideFrames(no) {
315 |   var el = getSlideEl(no);
316 |   if (!el) {
317 |     return;
318 |   }
319 | 
320 |   var frames = el.getElementsByTagName('iframe');
321 |   for (var i = 0, frame; frame = frames[i]; i++) {
322 |     enableFrame(frame);
323 |   }
324 | };
325 | 
326 | function disableFrame(frame) {
327 |   frame.src = 'about:blank';
328 | };
329 | 
330 | function enableFrame(frame) {
331 |   var src = frame._src;
332 | 
333 |   if (frame.src != src && src != 'about:blank') {
334 |     frame.src = src;
335 |   }
336 | };
337 | 
338 | function setupFrames() {
339 |   var frames = document.querySelectorAll('iframe');
340 |   for (var i = 0, frame; frame = frames[i]; i++) {
341 |     frame._src = frame.src;
342 |     disableFrame(frame);
343 |   }
344 | 
345 |   enableSlideFrames(curSlide);
346 |   enableSlideFrames(curSlide + 1);
347 |   enableSlideFrames(curSlide + 2);
348 | };
349 | 
350 | function setupInteraction() {
351 |   /* Clicking and tapping */
352 | 
353 |   var el = document.createElement('div');
354 |   el.className = 'slide-area';
355 |   el.id = 'prev-slide-area';
356 |   el.addEventListener('click', prevSlide, false);
357 |   document.querySelector('section.slides').appendChild(el);
358 | 
359 |   var el = document.createElement('div');
360 |   el.className = 'slide-area';
361 |   el.id = 'next-slide-area';
362 |   el.addEventListener('click', nextSlide, false);
363 |   document.querySelector('section.slides').appendChild(el);
364 | 
365 |   /* Swiping */
366 | 
367 |   document.body.addEventListener('touchstart', handleTouchStart, false);
368 | }
369 | 
370 | /* Hash functions */
371 | 
372 | function getCurSlideFromHash() {
373 |   var slideNo = parseInt(location.hash.substr(1));
374 | 
375 |   if (slideNo) {
376 |     curSlide = slideNo - 1;
377 |   } else {
378 |     curSlide = 0;
379 |   }
380 | };
381 | 
382 | function updateHash() {
383 |   location.replace('#' + (curSlide + 1));
384 | };
385 | 
386 | /* Event listeners */
387 | 
388 | function handleBodyKeyDown(event) {
389 |   // If we're in a code element, only handle pgup/down.
390 |   var inCode = event.target.classList.contains("code");
391 |   var hiddenBullets;
392 | 
393 |   switch (event.keyCode) {
394 |     case 39: // right arrow
395 |     case 13: // Enter
396 |     case 32: // space
397 |       if (inCode) break;
398 |     case 34: // PgDn
399 |       hiddenBullets = $('.current').find('li, p, .code, > .buttons').filter(':hidden');
400 |       if(hiddenBullets.length > 0) {
401 |           $(hiddenBullets[0]).show();
402 |       }
403 |       else {
404 |           nextSlide();
405 |       }
406 |       event.preventDefault();
407 |       break;
408 | 
409 |     case 37: // left arrow
410 |     case 8: // Backspace
411 |       if (inCode) break;
412 |     case 33: // PgUp
413 |       prevSlide();
414 |       event.preventDefault();
415 |       break;
416 | 
417 |     case 40: // down arrow
418 |       if (inCode) break;
419 |       nextSlide();
420 |       event.preventDefault();
421 |       break;
422 | 
423 |     case 38: // up arrow
424 |       if (inCode) break;
425 |       prevSlide();
426 |       event.preventDefault();
427 |       break;
428 |   }
429 | };
430 | 
431 | function addEventListeners() {
432 |   document.addEventListener('keydown', handleBodyKeyDown, false);
433 | };
434 | 
435 | /* Initialization */
436 | 
437 | function addFontStyle() {
438 |   var el = document.createElement('link');
439 |   el.rel = 'stylesheet';
440 |   el.type = 'text/css';
441 |   el.href = '//fonts.googleapis.com/css?family=' +
442 |             'Open+Sans:regular,semibold,italic,italicsemibold|Droid+Sans+Mono';
443 | 
444 |   document.body.appendChild(el);
445 | };
446 | 
447 | function addGeneralStyle() {
448 |   var el = document.createElement('link');
449 |   el.rel = 'stylesheet';
450 |   el.type = 'text/css';
451 |   el.href = PERMANENT_URL_PREFIX + 'styles.css';
452 |   document.body.appendChild(el);
453 | 
454 |   var el = document.createElement('meta');
455 |   el.name = 'viewport';
456 |   el.content = 'width=1100,height=750';
457 |   document.querySelector('head').appendChild(el);
458 | 
459 |   var el = document.createElement('meta');
460 |   el.name = 'apple-mobile-web-app-capable';
461 |   el.content = 'yes';
462 |   document.querySelector('head').appendChild(el);
463 | };
464 | 
465 | function addPrintStyle() {
466 |   var el = document.createElement('link');
467 |   el.rel = 'stylesheet';
468 |   el.type = 'text/css';
469 |   el.media = "print";
470 |   el.href = PERMANENT_URL_PREFIX + 'print.css';
471 |   document.body.appendChild(el);
472 | };
473 | 
474 | function handleDomLoaded() {
475 |   slideEls = document.querySelectorAll('section.slides > article');
476 | 
477 |   setupFrames();
478 | 
479 |   addFontStyle();
480 |   addGeneralStyle();
481 |   addPrintStyle();
482 |   addEventListeners();
483 | 
484 |   updateSlides();
485 | 
486 |   setupInteraction();
487 | 
488 |   document.body.classList.add('loaded');
489 | };
490 | 
491 | function initialize() {
492 |   getCurSlideFromHash();
493 | 
494 |   if (window['_DEBUG']) {
495 |     PERMANENT_URL_PREFIX = '../';
496 |   }
497 | 
498 |   if (window['_DCL']) {
499 |     handleDomLoaded();
500 |   } else {
501 |     document.addEventListener('DOMContentLoaded', handleDomLoaded, false);
502 |   }
503 | }
504 | 
505 | // If ?debug exists then load the script relative instead of absolute
506 | if (!window['_DEBUG'] && document.location.href.indexOf('?debug') !== -1) {
507 |   document.addEventListener('DOMContentLoaded', function() {
508 |     // Avoid missing the DomContentLoaded event
509 |     window['_DCL'] = true
510 |   }, false);
511 | 
512 |   window['_DEBUG'] = true;
513 |   var script = document.createElement('script');
514 |   script.type = 'text/javascript';
515 |   script.src = '../slides.js';
516 |   var s = document.getElementsByTagName('script')[0];
517 |   s.parentNode.insertBefore(script, s);
518 | 
519 |   // Remove this script
520 |   s.parentNode.removeChild(s);
521 | } else {
522 |   initialize();
523 | }
524 | 


--------------------------------------------------------------------------------
/assets/static/styles.css:
--------------------------------------------------------------------------------
  1 | /* Framework */
  2 | 
  3 | html {
  4 |   height: 100%;
  5 | }
  6 | 
  7 | body {
  8 |   margin: 0;
  9 |   padding: 0;
 10 | 
 11 |   display: block !important;
 12 | 
 13 |   height: 100%;
 14 |   min-height: 740px;
 15 | 
 16 |   overflow-x: hidden;
 17 |   overflow-y: auto;
 18 | 
 19 |   background: rgb(215, 215, 215);
 20 |   background: -o-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
 21 |   background: -moz-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
 22 |   background: -webkit-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
 23 |   background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 500, from(rgb(240, 240, 240)), to(rgb(190, 190, 190)));
 24 | 
 25 |   -webkit-font-smoothing: antialiased;
 26 | }
 27 | 
 28 | .slides {
 29 |   width: 100%;
 30 |   height: 100%;
 31 |   left: 0;
 32 |   top: 0;
 33 | 
 34 |   position: absolute;
 35 | 
 36 |   -webkit-transform: translate3d(0, 0, 0);
 37 | }
 38 | 
 39 | .slides > article {
 40 |   display: block;
 41 | 
 42 |   position: absolute;
 43 |   overflow: hidden;
 44 | 
 45 |   width: 900px;
 46 |   height: 700px;
 47 | 
 48 |   left: 50%;
 49 |   top: 50%;
 50 | 
 51 |   margin-left: -450px;
 52 |   margin-top: -350px;
 53 | 
 54 |   padding: 40px 60px;
 55 | 
 56 |   box-sizing: border-box;
 57 |   -o-box-sizing: border-box;
 58 |   -moz-box-sizing: border-box;
 59 |   -webkit-box-sizing: border-box;
 60 | 
 61 |   border-radius: 10px;
 62 |   -o-border-radius: 10px;
 63 |   -moz-border-radius: 10px;
 64 |   -webkit-border-radius: 10px;
 65 | 
 66 |   background-color: white;
 67 | 
 68 |   border: 1px solid rgba(0, 0, 0, .3);
 69 | 
 70 |   transition: transform .3s ease-out;
 71 |   -o-transition: -o-transform .3s ease-out;
 72 |   -moz-transition: -moz-transform .3s ease-out;
 73 |   -webkit-transition: -webkit-transform .3s ease-out;
 74 | }
 75 | .slides.layout-widescreen > article {
 76 |   margin-left: -550px;
 77 |   width: 1100px;
 78 | }
 79 | .slides.layout-faux-widescreen > article {
 80 |   margin-left: -550px;
 81 |   width: 1100px;
 82 | 
 83 |   padding: 40px 160px;
 84 | }
 85 | 
 86 | .slides.layout-widescreen > article:not(.nobackground):not(.biglogo),
 87 | .slides.layout-faux-widescreen > article:not(.nobackground):not(.biglogo) {
 88 |   background-position-x: 0, 840px;
 89 | }
 90 | 
 91 | /* Clickable/tappable areas */
 92 | 
 93 | .slide-area {
 94 |   z-index: 1000;
 95 | 
 96 |   position: absolute;
 97 |   left: 0;
 98 |   top: 0;
 99 |   width: 150px;
100 |   height: 700px;
101 | 
102 |   left: 50%;
103 |   top: 50%;
104 | 
105 |   cursor: pointer;
106 |   margin-top: -350px;
107 | 
108 |   tap-highlight-color: transparent;
109 |   -o-tap-highlight-color: transparent;
110 |   -moz-tap-highlight-color: transparent;
111 |   -webkit-tap-highlight-color: transparent;
112 | }
113 | #prev-slide-area {
114 |   margin-left: -550px;
115 | }
116 | #next-slide-area {
117 |   margin-left: 400px;
118 | }
119 | .slides.layout-widescreen #prev-slide-area,
120 | .slides.layout-faux-widescreen #prev-slide-area {
121 |   margin-left: -650px;
122 | }
123 | .slides.layout-widescreen #next-slide-area,
124 | .slides.layout-faux-widescreen #next-slide-area {
125 |   margin-left: 500px;
126 | }
127 | 
128 | /* Slides */
129 | 
130 | .slides > article, .slides > article li, .slides > article > p,
131 | .slides > article > div.buttons, .slides > article > div.code {
132 |   display: none;
133 | }
134 | .slides > article.far-past {
135 |   display: block;
136 |   transform: translate(-2040px);
137 |   -o-transform: translate(-2040px);
138 |   -moz-transform: translate(-2040px);
139 |   -webkit-transform: translate3d(-2040px, 0, 0);
140 | }
141 | .slides > article.past {
142 |   display: block;
143 |   transform: translate(-1020px);
144 |   -o-transform: translate(-1020px);
145 |   -moz-transform: translate(-1020px);
146 |   -webkit-transform: translate3d(-1020px, 0, 0);
147 | }
148 | .slides > article.current {
149 |   display: block;
150 |   transform: translate(0);
151 |   -o-transform: translate(0);
152 |   -moz-transform: translate(0);
153 |   -webkit-transform: translate3d(0, 0, 0);
154 | }
155 | .slides > article.next {
156 |   display: block;
157 |   transform: translate(1020px);
158 |   -o-transform: translate(1020px);
159 |   -moz-transform: translate(1020px);
160 |   -webkit-transform: translate3d(1020px, 0, 0);
161 | }
162 | .slides > article.far-next {
163 |   display: block;
164 |   transform: translate(2040px);
165 |   -o-transform: translate(2040px);
166 |   -moz-transform: translate(2040px);
167 |   -webkit-transform: translate3d(2040px, 0, 0);
168 | }
169 | 
170 | .slides.layout-widescreen > article.far-past,
171 | .slides.layout-faux-widescreen > article.far-past {
172 |   display: block;
173 |   transform: translate(-2260px);
174 |   -o-transform: translate(-2260px);
175 |   -moz-transform: translate(-2260px);
176 |   -webkit-transform: translate3d(-2260px, 0, 0);
177 | }
178 | .slides.layout-widescreen > article.past,
179 | .slides.layout-faux-widescreen > article.past {
180 |   display: block;
181 |   transform: translate(-1130px);
182 |   -o-transform: translate(-1130px);
183 |   -moz-transform: translate(-1130px);
184 |   -webkit-transform: translate3d(-1130px, 0, 0);
185 | }
186 | .slides.layout-widescreen > article.current,
187 | .slides.layout-faux-widescreen > article.current {
188 |   display: block;
189 |   transform: translate(0);
190 |   -o-transform: translate(0);
191 |   -moz-transform: translate(0);
192 |   -webkit-transform: translate3d(0, 0, 0);
193 | }
194 | .slides.layout-widescreen > article.next,
195 | .slides.layout-faux-widescreen > article.next {
196 |   display: block;
197 |   transform: translate(1130px);
198 |   -o-transform: translate(1130px);
199 |   -moz-transform: translate(1130px);
200 |   -webkit-transform: translate3d(1130px, 0, 0);
201 | }
202 | .slides.layout-widescreen > article.far-next,
203 | .slides.layout-faux-widescreen > article.far-next {
204 |   display: block;
205 |   transform: translate(2260px);
206 |   -o-transform: translate(2260px);
207 |   -moz-transform: translate(2260px);
208 |   -webkit-transform: translate3d(2260px, 0, 0);
209 | }
210 | 
211 | /* Styles for slides */
212 | 
213 | .slides > article {
214 |   font-family: 'Open Sans', Arial, sans-serif;
215 | 
216 |   color: black;
217 |   text-shadow: 0 1px 1px rgba(0, 0, 0, .1);
218 | 
219 |   font-size: 26px;
220 |   line-height: 36px;
221 | 
222 |   letter-spacing: -1px;
223 | }
224 | 
225 | b {
226 |   font-weight: 600;
227 | }
228 | 
229 | a {
230 |   color: rgb(0, 102, 204);
231 |   text-decoration: none;
232 | }
233 | a:visited {
234 |   color: rgba(0, 102, 204, .75);
235 | }
236 | a:hover {
237 |   color: black;
238 | }
239 | 
240 | p {
241 |   margin: 0;
242 |   padding: 0;
243 | 
244 |   margin-top: 20px;
245 | }
246 | p:first-child {
247 |   margin-top: 0;
248 | }
249 | 
250 | h1 {
251 |   font-size: 60px;
252 |   line-height: 60px;
253 | 
254 |   padding: 0;
255 |   margin: 0;
256 |   margin-top: 200px;
257 |   margin-bottom: 5px;
258 |   padding-right: 40px;
259 | 
260 |   font-weight: 600;
261 | 
262 |   letter-spacing: -3px;
263 | 
264 |   color: rgb(51, 51, 51);
265 | }
266 | 
267 | h2 {
268 |   font-size: 45px;
269 |   line-height: 45px;
270 | 
271 |   position: absolute;
272 |   bottom: 150px;
273 | 
274 |   padding: 0;
275 |   margin: 0;
276 |   padding-right: 40px;
277 | 
278 |   font-weight: 600;
279 | 
280 |   letter-spacing: -2px;
281 | 
282 |   color: rgb(51, 51, 51);
283 | }
284 | 
285 | h3 {
286 |   font-size: 30px;
287 |   line-height: 36px;
288 | 
289 |   padding: 0;
290 |   margin: 0;
291 |   padding-right: 40px;
292 | 
293 |   font-weight: 600;
294 | 
295 |   letter-spacing: -1px;
296 | 
297 |   color: rgb(51, 51, 51);
298 | }
299 | 
300 | ul {
301 |   margin: 0;
302 |   padding: 0;
303 |   margin-top: 20px;
304 |   margin-left: 1.5em;
305 | }
306 | li {
307 |   padding: 0;
308 |   margin: 0 0 .5em 0;
309 | }
310 | 
311 | div.code {
312 |   padding: 5px 10px;
313 |   margin-top: 20px;
314 |   margin-bottom: 20px;
315 |   overflow: hidden;
316 | 
317 |   background: rgb(240, 240, 240);
318 |   border: 1px solid rgb(224, 224, 224);
319 | }
320 | pre {
321 |   margin: 0;
322 |   padding: 0;
323 | 
324 |   font-family: 'Droid Sans Mono', 'Courier New', monospace;
325 |   font-size: 18px;
326 |   line-height: 24px;
327 |   letter-spacing: -1px;
328 | 
329 |   color: black;
330 | }
331 | 
332 | code {
333 |   font-size: 95%;
334 |   font-family: 'Droid Sans Mono', 'Courier New', monospace;
335 | 
336 |   color: black;
337 | }
338 | 
339 | article > .image {
340 |   	text-align: center;
341 |     margin-top: 40px;
342 | }
343 | 
344 | table {
345 |   width: 100%;
346 |   border-collapse: collapse;
347 |   margin-top: 40px;
348 | }
349 | th {
350 |   font-weight: 600;
351 |   text-align: left;
352 | }
353 | td,
354 | th {
355 |   border: 1px solid rgb(224, 224, 224);
356 |   padding: 5px 10px;
357 |   vertical-align: top;
358 | }
359 | 
360 | p.link {
361 |   margin-left: 20px;
362 | }
363 | 
364 | /* Code */
365 | div.code {
366 |   outline: 0px solid transparent;
367 | }
368 | div.playground {
369 |   position: relative;
370 | }
371 | div.output {
372 |   position: absolute;
373 |   left: 50%;
374 |   top: 50%;
375 |   right: 40px;
376 |   bottom: 40px;
377 |   background: #202020;
378 |   padding: 5px 10px;
379 |   z-index: 2;
380 | 
381 |   border-radius: 10px;
382 |   -o-border-radius: 10px;
383 |   -moz-border-radius: 10px;
384 |   -webkit-border-radius: 10px;
385 | 
386 | }
387 | div.output pre {
388 |   margin: 0;
389 |   padding: 0;
390 |   background: none;
391 |   border: none;
392 |   width: 100%;
393 |   height: 100%;
394 |   overflow: auto;
395 | }
396 | div.output .stdout, div.output pre {
397 |   color: #e6e6e6;
398 | }
399 | div.output .stderr, div.output .error {
400 |   color: rgb(244, 74, 63);
401 | }
402 | div.output .system, div.output .exit {
403 |   color: rgb(255, 209, 77)
404 | }
405 | .buttons {
406 |   position: relative;
407 |   float: right;
408 |   top: -60px;
409 |   right: 10px;
410 | }
411 | div.output .buttons {
412 |   position: absolute;
413 |   float: none;
414 |   top: auto;
415 |   right: 5px;
416 |   bottom: 5px;
417 | }
418 | 
419 | /* Presenter details */
420 | .presenter {
421 | 	margin-top: 20px;
422 | }
423 | .presenter p,
424 | .presenter .link {
425 | 	margin: 0;
426 | 	font-size: 28px;
427 | 	line-height: 1.2em;
428 | }
429 | 
430 | /* Output resize details */
431 | .ui-resizable-handle {
432 |   position: absolute;
433 | }
434 | .ui-resizable-n {
435 |   cursor: n-resize;
436 |   height: 7px;
437 |   width: 100%;
438 |   top: -5px;
439 |   left: 0;
440 | }
441 | .ui-resizable-w {
442 |   cursor: w-resize;
443 |   width: 7px;
444 |   left: -5px;
445 |   top: 0;
446 |   height: 100%;
447 | }
448 | .ui-resizable-nw {
449 |   cursor: nw-resize;
450 |   width: 9px;
451 |   height: 9px;
452 |   left: -5px;
453 |   top: -5px;
454 | }
455 | 


--------------------------------------------------------------------------------
/assets/templates/action.tmpl:
--------------------------------------------------------------------------------
 1 | {/*
 2 | This is the action template.
 3 | It determines how the formatting actions are rendered.
 4 | */}
 5 | 
 6 | {{define "section"}}
 7 |   {{.FormattedNumber}} {{.Title}}
 8 |   {{range .Elem}}{{elem $.Template .}}{{end}}
 9 | {{end}}
10 | 
11 | {{define "list"}}
12 |   
    13 | {{range .Bullet}} 14 |
  • {{style .}}
  • 15 | {{end}} 16 |
17 | {{end}} 18 | 19 | {{define "text"}} 20 | {{if .Pre}} 21 |
{{range .Lines}}{{.}}{{end}}
22 | {{else}} 23 |

24 | {{range $i, $l := .Lines}}{{if $i}}{{template "newline"}} 25 | {{end}}{{style $l}}{{end}} 26 |

27 | {{end}} 28 | {{end}} 29 | 30 | {{define "code"}} 31 |
{{.Text}}
32 | {{end}} 33 | 34 | {{define "image"}} 35 |
36 | 37 |
38 | {{end}} 39 | 40 | {{define "iframe"}} 41 | 42 | {{end}} 43 | 44 | {{define "link"}}{{end}} 45 | 46 | {{define "html"}}{{.HTML}}{{end}} 47 | -------------------------------------------------------------------------------- /assets/templates/article.tmpl: -------------------------------------------------------------------------------- 1 | {/* This is the article template. It defines how articles are formatted. */} 2 | 3 | {{define "root"}} 4 | 5 | 6 | 7 | {{.Title}} 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
{{.Title}} 16 | {{with .Subtitle}}{{.}}{{end}} 17 |
18 |
19 |
20 |
21 |
22 | {{with .Sections}} 23 |
24 | {{template "TOC" .}} 25 |
26 | {{end}} 27 | 28 | {{range .Sections}} 29 | {{elem $.Template .}} 30 | {{end}}{{/* of Section block */}} 31 | 32 | {{if .Authors}} 33 |

Authors

34 | {{range .Authors}} 35 |
36 | {{range .Elem}}{{elem $.Template .}}{{end}} 37 |
38 | {{end}} 39 | {{end}} 40 |
41 |
42 | 43 | 44 | 45 | {{end}} 46 | 47 | {{define "TOC"}} 48 |
    49 | {{range .}} 50 |
  • {{.Title}}
  • 51 | {{with .Sections}}{{template "TOC" .}}{{end}} 52 | {{end}} 53 |
54 | {{end}} 55 | 56 | {{define "newline"}} 57 | {{/* No automatic line break. Paragraphs are free-form. */}} 58 | {{end}} 59 | -------------------------------------------------------------------------------- /assets/templates/dir.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Talks - The Go Programming Language 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | 22 | 23 |
24 | 25 |
26 | 27 |
28 | 29 |

Go talks

30 | 31 | {{with .Path}}

{{.}}

{{end}} 32 | 33 | {{with .Articles}} 34 |

Articles:

35 |
36 | {{range .}} 37 |
{{.Name}}: {{.Title}}
38 | {{end}} 39 |
40 | {{end}} 41 | 42 | {{with .Slides}} 43 |

Slide decks:

44 |
45 | {{range .}} 46 |
{{.Name}}: {{.Title}}
47 | {{end}} 48 |
49 | {{end}} 50 | 51 | {{with .Other}} 52 |

Files:

53 |
54 | {{range .}} 55 |
{{.Name}}
56 | {{end}} 57 |
58 | {{end}} 59 | 60 | {{with .Dirs}} 61 |

Sub-directories:

62 |
63 | {{range .}} 64 |
{{.Name}}
65 | {{end}} 66 |
67 | {{end}} 68 | 69 |
70 | 71 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /assets/templates/slides.tmpl: -------------------------------------------------------------------------------- 1 | {/* This is the slide template. It defines how presentations are formatted. */} 2 | 3 | {{define "root"}} 4 | 5 | 6 | 7 | {{.Title}} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 |
18 |

{{.Title}}

19 | {{with .Subtitle}}

{{.}}

{{end}} 20 | {{if not .Time.IsZero}}

{{.Time.Format "2 January 2006"}}

{{end}} 21 | {{range .Authors}} 22 |
23 | {{range .TextElem}}{{elem $.Template .}}{{end}} 24 |
25 | {{end}} 26 |
27 | 28 | {{range $i, $s := .Sections}} 29 | 30 |
31 | {{if $s.Elem}} 32 |

{{$s.Title}}

33 | {{range $s.Elem}}{{elem $.Template .}}{{end}} 34 | {{else}} 35 |

{{$s.Title}}

36 | {{end}} 37 |
38 | 39 | {{end}}{{/* of Slide block */}} 40 | 41 |
42 |

Въпроси?

43 | {{range .Authors}} 44 |
45 | {{range .Elem}}{{elem $.Template .}}{{end}} 46 |
47 | {{end}} 48 | 49 |
50 | 51 | 52 | {{if .PlayEnabled}} 53 | 54 | {{end}} 55 | 56 | {{end}} 57 | 58 | {{define "newline"}} 59 |
60 | {{end}} 61 | -------------------------------------------------------------------------------- /code/cgo-runtime/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: main 3 | 4 | main: foo.o bar.c 5 | gcc foo.o bar.c -o main 6 | 7 | foo.o: foo.go 8 | gccgo -c foo.go -o foo.o -fgo-prefix=example 9 | 10 | clean: 11 | rm -f main *.o 12 | -------------------------------------------------------------------------------- /code/cgo-runtime/bar.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | extern int go_add(int, int) __asm__ ("example.main.Add"); 5 | 6 | int main() { 7 | int x = go_add(2, 3); 8 | printf("Result: %d\n", x); 9 | } 10 | -------------------------------------------------------------------------------- /code/cgo-runtime/bar.s: -------------------------------------------------------------------------------- 1 | .file "bar.c" 2 | .section .rodata 3 | .LC0: 4 | .string "Result: %d\n" 5 | .text 6 | .globl main 7 | .type main, @function 8 | main: 9 | .LFB0: 10 | .cfi_startproc 11 | pushq %rbp 12 | .cfi_def_cfa_offset 16 13 | .cfi_offset 6, -16 14 | movq %rsp, %rbp 15 | .cfi_def_cfa_register 6 16 | subq $16, %rsp 17 | movl $3, %esi 18 | movl $2, %edi 19 | call example.main.Add 20 | movl %eax, -4(%rbp) 21 | movl -4(%rbp), %eax 22 | movl %eax, %esi 23 | movl $.LC0, %edi 24 | movl $0, %eax 25 | call printf 26 | leave 27 | .cfi_def_cfa 7, 8 28 | ret 29 | .cfi_endproc 30 | .LFE0: 31 | .size main, .-main 32 | .ident "GCC: (GNU) 4.8.2" 33 | .section .note.GNU-stack,"",@progbits 34 | -------------------------------------------------------------------------------- /code/cgo-runtime/cgo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | #include 5 | */ 6 | import "C" // HL 7 | 8 | func Random() int { 9 | return int(C.random()) 10 | } 11 | 12 | func Seed(i int) { 13 | C.srandom(C.uint(i)) 14 | } 15 | -------------------------------------------------------------------------------- /code/cgo-runtime/foo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func Add(a, b int) int { 4 | return a + b 5 | } 6 | -------------------------------------------------------------------------------- /code/cgo-runtime/foo.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fmi/go-lectures/85aa482166eaee38d9931c3b2f1c43fe0304b021/code/cgo-runtime/foo.o -------------------------------------------------------------------------------- /code/cgo-runtime/foo.s: -------------------------------------------------------------------------------- 1 | 2 | --- prog list "Add" --- 3 | 0000 (foo.go:3) TEXT Add+0(SB),$0-24 4 | 0001 (foo.go:3) LOCALS ,$0 5 | 0002 (foo.go:3) TYPE a+0(FP){int},$8 6 | 0003 (foo.go:3) TYPE b+8(FP){int},$8 7 | 0004 (foo.go:3) TYPE ~anon2+16(FP){int},$8 8 | 0005 (foo.go:4) MOVQ a+0(FP),BX 9 | 0006 (foo.go:4) MOVQ b+8(FP),BP 10 | 0007 (foo.go:4) ADDQ BP,BX 11 | 0008 (foo.go:4) MOVQ BX,~anon2+16(FP) 12 | 0009 (foo.go:4) RET , 13 | 14 | --- prog list "init" --- 15 | 0010 (foo.go:5) TEXT init+0(SB),$0-0 16 | 0011 (foo.go:5) MOVBQZX initdone·+0(SB),AX 17 | 0012 (foo.go:5) LOCALS ,$0 18 | 0013 (foo.go:5) CMPB AX,$0 19 | 0014 (foo.go:5) JEQ ,20 20 | 0015 (foo.go:5) CMPB AX,$2 21 | 0016 (foo.go:5) JNE ,18 22 | 0017 (foo.go:5) RET , 23 | 0018 (foo.go:5) CALL ,runtime.throwinit+0(SB) 24 | 0019 (foo.go:5) UNDEF , 25 | 0020 (foo.go:5) MOVB $2,initdone·+0(SB) 26 | 0021 (foo.go:5) RET , 27 | -------------------------------------------------------------------------------- /code/cgo-runtime/func_point.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // typedef int (*intFunc) (); 4 | // 5 | // int 6 | // bridge_int_func(intFunc f) 7 | // { 8 | // return f(); 9 | // } 10 | // 11 | // int fortytwo() 12 | // { 13 | // return 42; 14 | // } 15 | import "C" 16 | import "fmt" 17 | 18 | func main() { 19 | f := C.intFunc(C.fortytwo) 20 | fmt.Println(int(C.bridge_int_func(f))) 21 | // Output: 42 22 | } 23 | -------------------------------------------------------------------------------- /code/cgo-runtime/gcc_foo.s: -------------------------------------------------------------------------------- 1 | .file "foo.go" 2 | .section .go_export,"",@progbits 3 | .ascii "v1;\n" 4 | .ascii "package " 5 | .ascii "main" 6 | .ascii ";\n" 7 | .ascii "pkgpath " 8 | .ascii "main" 9 | .ascii ";\n" 10 | .ascii "priority 1;\n" 11 | .ascii "func " 12 | .ascii "Add" 13 | .ascii " (" 14 | .ascii "a" 15 | .ascii " " 16 | .ascii "" 17 | .ascii ", " 18 | .ascii "b" 19 | .ascii " " 20 | .ascii "" 21 | .ascii ")" 22 | .ascii " " 23 | .ascii "" 24 | .ascii ";\n" 25 | .ascii "checksum FD80E13A21B63639D0FE0BD8A2F5B84400D3F2A9;\n" 26 | .globl main.Add$descriptor 27 | .section .rodata.main.Add$descriptor,"a",@progbits 28 | .align 8 29 | .type main.Add$descriptor, @object 30 | .size main.Add$descriptor, 8 31 | main.Add$descriptor: 32 | .quad main.Add 33 | .text 34 | .globl main.Add 35 | .type main.Add, @function 36 | main.Add: 37 | .LFB0: 38 | .cfi_startproc 39 | cmpq %fs:112, %rsp 40 | jae .L3 41 | movl $8, %r10d 42 | movl $0, %r11d 43 | call __morestack 44 | ret 45 | .L3: 46 | pushq %rbp 47 | .cfi_def_cfa_offset 16 48 | .cfi_offset 6, -16 49 | movq %rsp, %rbp 50 | .cfi_def_cfa_register 6 51 | movq %rdi, -24(%rbp) 52 | movq %rsi, -32(%rbp) 53 | movq $0, -8(%rbp) 54 | movq -32(%rbp), %rax 55 | movq -24(%rbp), %rdx 56 | addq %rdx, %rax 57 | movq %rax, -8(%rbp) 58 | movq -8(%rbp), %rax 59 | popq %rbp 60 | .cfi_def_cfa 7, 8 61 | ret 62 | .cfi_endproc 63 | .LFE0: 64 | .size main.Add, .-main.Add 65 | .globl __go_init_main 66 | .type __go_init_main, @function 67 | __go_init_main: 68 | .LFB1: 69 | .cfi_startproc 70 | cmpq %fs:112, %rsp 71 | jae .L5 72 | movl $8, %r10d 73 | movl $0, %r11d 74 | call __morestack 75 | ret 76 | .L5: 77 | pushq %rbp 78 | .cfi_def_cfa_offset 16 79 | .cfi_offset 6, -16 80 | movq %rsp, %rbp 81 | .cfi_def_cfa_register 6 82 | popq %rbp 83 | .cfi_def_cfa 7, 8 84 | ret 85 | .cfi_endproc 86 | .LFE1: 87 | .size __go_init_main, .-__go_init_main 88 | .section .note.GNU-split-stack,"",@progbits 89 | .ident "GCC: (GNU) 4.8.2" 90 | .section .note.GNU-stack,"",@progbits 91 | -------------------------------------------------------------------------------- /code/cgo-runtime/goc/goc.c: -------------------------------------------------------------------------------- 1 | #include "goc.h" 2 | #include 3 | 4 | int main() { 5 | printf("Hi, I am a C program.\n"); 6 | GoString name = {"Doycho", 4}; 7 | GreetFromGo(name); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /code/cgo-runtime/goc/goc.go: -------------------------------------------------------------------------------- 1 | package goc 2 | 3 | import "C" 4 | 5 | //export GreetFromGo 6 | func GreetFromGo(name string) { 7 | println("Hello from Go, ", name) 8 | } 9 | 10 | func main() { 11 | // Needed by cgo in order to generate a library 12 | } 13 | -------------------------------------------------------------------------------- /code/cgo-runtime/goc/goc.h: -------------------------------------------------------------------------------- 1 | /* Created by "go tool cgo" - DO NOT EDIT. */ 2 | 3 | /* package command-line-arguments */ 4 | 5 | /* Start of preamble from import "C" comments. */ 6 | 7 | 8 | 9 | 10 | /* End of preamble from import "C" comments. */ 11 | 12 | 13 | /* Start of boilerplate cgo prologue. */ 14 | 15 | #ifndef GO_CGO_PROLOGUE_H 16 | #define GO_CGO_PROLOGUE_H 17 | 18 | typedef signed char GoInt8; 19 | typedef unsigned char GoUint8; 20 | typedef short GoInt16; 21 | typedef unsigned short GoUint16; 22 | typedef int GoInt32; 23 | typedef unsigned int GoUint32; 24 | typedef long long GoInt64; 25 | typedef unsigned long long GoUint64; 26 | typedef GoInt64 GoInt; 27 | typedef GoUint64 GoUint; 28 | typedef __SIZE_TYPE__ GoUintptr; 29 | typedef float GoFloat32; 30 | typedef double GoFloat64; 31 | typedef __complex float GoComplex64; 32 | typedef __complex double GoComplex128; 33 | 34 | // static assertion to make sure the file is being used on architecture 35 | // at least with matching size of GoInt. 36 | typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; 37 | 38 | typedef struct { char *p; GoInt n; } GoString; 39 | typedef void *GoMap; 40 | typedef void *GoChan; 41 | typedef struct { void *t; void *v; } GoInterface; 42 | typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; 43 | 44 | #endif 45 | 46 | /* End of boilerplate cgo prologue. */ 47 | 48 | #ifdef __cplusplus 49 | extern "C" { 50 | #endif 51 | 52 | 53 | extern void GreetFromGo(GoString p0); 54 | 55 | #ifdef __cplusplus 56 | } 57 | #endif 58 | -------------------------------------------------------------------------------- /code/cgo-runtime/math/math.go: -------------------------------------------------------------------------------- 1 | package math 2 | 3 | /* 4 | #cgo LDFLAGS: -lm 5 | #include 6 | */ 7 | import "C" 8 | 9 | func pow(a, b float64) float64 { 10 | return float64(C.pow(C.double(a), C.double(b))) 11 | 12 | } 13 | -------------------------------------------------------------------------------- /code/cgo-runtime/math/math_test.go: -------------------------------------------------------------------------------- 1 | package math 2 | 3 | import "testing" 4 | 5 | func TestPow(t *testing.T) { 6 | a := pow(2.0, 4.0) 7 | 8 | if a != 16.0 { 9 | t.Fatalf("we suck at math!") 10 | 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /code/cgo-runtime/unsafe.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | #include 5 | */ 6 | import "C" 7 | import ( 8 | "fmt" 9 | "unsafe" 10 | ) 11 | 12 | func main() { 13 | cs := C.CString("42") // alloc on C's heap 14 | defer C.free(unsafe.Pointer(cs)) // don't leak 15 | answer := C.atoi(cs) 16 | fmt.Println(answer) 17 | } 18 | -------------------------------------------------------------------------------- /code/cgo-runtime/unsafe_2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "unsafe" 4 | import "fmt" 5 | 6 | type slice struct { 7 | array unsafe.Pointer 8 | size, _cap int 9 | } 10 | 11 | func main() { 12 | var p = []string{"Hello", " "} 13 | p = append(p, "World!") 14 | var s = (*slice)(unsafe.Pointer(&p)) 15 | var sizeOfString = unsafe.Sizeof("") 16 | fmt.Printf("size=%d, cap=%d\n", s.size, s._cap) 17 | for i := 0; s.size > i; i++ { 18 | fmt.Printf("[%d]: `%s`\n", i, 19 | *(*string)(unsafe.Pointer(uintptr(s.array) + uintptr(i)*sizeOfString))) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /code/concurrency101/boring-with-sync.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | var wg sync.WaitGroup // HL 12 | 13 | boring := func(msg string) { 14 | defer wg.Done() // HL 15 | for i := 0; i < 8; i++ { 16 | fmt.Println(msg, i) 17 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 18 | } 19 | } 20 | 21 | wg.Add(1) // HL 22 | go boring("boring!") 23 | fmt.Println("Waiting for the boring function to do its work") 24 | wg.Wait() // HL 25 | } 26 | -------------------------------------------------------------------------------- /code/concurrency101/boring.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | boring("boring!") 10 | } 11 | 12 | func boring(msg string) { 13 | for i := 0; ; i++ { 14 | fmt.Println(msg, i) 15 | time.Sleep(1 * time.Second) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /code/concurrency101/c_fork.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | printf("before\n"); 6 | if (fork()) 7 | printf("parent\n"); 8 | else 9 | printf("child\n"); 10 | printf("both\n"); 11 | } 12 | -------------------------------------------------------------------------------- /code/concurrency101/c_fork_sync.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | pid_t pid = fork(); 7 | if (pid == 0) { 8 | printf("child sleeping...\n"); 9 | execl("/bin/sleep", "/bin/sleep", "2", (char *) 0); 10 | } else { 11 | waitpid(pid, NULL, 0); 12 | } 13 | printf("done!\n"); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /code/concurrency101/c_threads.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void *ticker(void *x_void_ptr) { 6 | while (42) { 7 | sleep(1); 8 | printf("tick\n"); 9 | } 10 | 11 | return NULL; 12 | } 13 | 14 | int main() { 15 | pthread_t ticker_thread; 16 | 17 | if(pthread_create(&ticker_thread, NULL, ticker, NULL)) { 18 | fprintf(stderr, "Error creating thread\n"); 19 | return 1; 20 | } 21 | 22 | if(pthread_join(ticker_thread, NULL)) { 23 | fprintf(stderr, "Error joining thread\n"); 24 | return 2; 25 | } 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /code/concurrency101/channel-simple-demo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | ch := make(chan string) 7 | go func() { 8 | fmt.Printf("Goroutine received: %s\n", <-ch) 9 | ch <- "Hello from the other side" 10 | }() 11 | 12 | ch <- "Hello, can you hear me?" 13 | fmt.Printf("Main received: %s", <-ch) 14 | } 15 | -------------------------------------------------------------------------------- /code/concurrency101/closing-channels.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | ch := make(chan string) 7 | 8 | go func(output chan string) { 9 | for i := 0; i < 5; i++ { 10 | output <- fmt.Sprintf("sending N=%d", i) 11 | } 12 | close(output) 13 | }(ch) 14 | 15 | for i := 0; i < 7; i++ { 16 | val, ok := <-ch 17 | fmt.Printf("Recieved: %#v, %#v\n", val, ok) 18 | } 19 | 20 | ch <- fmt.Sprintf("where is your towel?") 21 | } 22 | -------------------------------------------------------------------------------- /code/concurrency101/deadlock.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan int) 5 | c <- 42 6 | val := <-c 7 | println(val) 8 | } 9 | -------------------------------------------------------------------------------- /code/concurrency101/generator.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func init() { 10 | rand.Seed(time.Now().Unix()) 11 | } 12 | 13 | func randomFeed(count, max int) <-chan int { 14 | c := make(chan int) 15 | 16 | go func() { 17 | for i := 0; i < count; i++ { 18 | c <- rand.Intn(max) 19 | } 20 | close(c) 21 | }() 22 | 23 | return c 24 | } 25 | 26 | func main() { 27 | feed := randomFeed(10, 100) 28 | for v := range feed { 29 | fmt.Println(v) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /code/concurrency101/go-less-boring-sleep.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | go boring("boring!") // HL 11 | fmt.Println("Listening.") 12 | time.Sleep(2 * time.Second) // HL 13 | fmt.Println("You are way too boring. I am leaving.") 14 | } 15 | 16 | func boring(msg string) { 17 | for i := 0; ; i++ { 18 | fmt.Println(msg, i) 19 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /code/concurrency101/go-less-boring.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | go boring("boring!") // HL 11 | } 12 | 13 | func boring(msg string) { 14 | for i := 0; ; i++ { 15 | fmt.Println(msg, i) 16 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /code/concurrency101/less-boring.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | boring("boring!") 11 | } 12 | 13 | func boring(msg string) { 14 | for i := 0; ; i++ { 15 | fmt.Println(msg, i) 16 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /code/concurrency101/map-keys.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | slice := []int{1, 2, 3} 9 | fmt.Printf("%+v\n", map[*[]int]int{&slice: 5}) 10 | } 11 | -------------------------------------------------------------------------------- /code/concurrency101/python_threads.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import time 4 | import threading 5 | 6 | def ticker(): 7 | while 42: 8 | print("Tick!") 9 | time.sleep(1) 10 | 11 | thread = threading.Thread(target=ticker) 12 | thread.daemon = True 13 | thread.start() 14 | 15 | # Or using sublcassing: 16 | 17 | class TickerThread(threading.Thread): 18 | def run(self): 19 | while 42: 20 | print("Tick!") 21 | time.sleep(1) 22 | 23 | thread = TickerThread() 24 | thread.start() 25 | # ... 26 | thread.join() 27 | -------------------------------------------------------------------------------- /code/concurrency101/synchronization.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | c := make(chan int) 10 | 11 | go func() { 12 | fmt.Println("SCV: Reportin' for duty") 13 | time.Sleep(2 * time.Second) 14 | fmt.Println("SCV: Job's finished!") 15 | c <- 1 16 | }() 17 | 18 | fmt.Println("Main does other time-consuming work...") 19 | time.Sleep(1 * time.Second) 20 | fmt.Println("Main is done") 21 | <-c 22 | 23 | fmt.Println("Everyone is done") 24 | } 25 | -------------------------------------------------------------------------------- /code/concurrency102/boring-with-sync.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | var wg sync.WaitGroup // HL 12 | 13 | boring := func(msg string) { 14 | for i := 0; i < 8; i++ { 15 | fmt.Println(msg, i) 16 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 17 | } 18 | wg.Done() // HL 19 | } 20 | 21 | wg.Add(1) // HL 22 | go boring("boring!") 23 | fmt.Println("Waiting for the boring function to do its work") 24 | wg.Wait() // HL 25 | } 26 | -------------------------------------------------------------------------------- /code/concurrency102/closed.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | var wg sync.WaitGroup 9 | 10 | func waitForMe(ch chan int) { 11 | defer wg.Done() 12 | for { 13 | value, ok := <-ch 14 | if ok { 15 | fmt.Println(value) 16 | } else { 17 | fmt.Println("My channel has closed and all I got is this:", value) 18 | return 19 | } 20 | } 21 | } 22 | 23 | func main() { 24 | wg.Add(1) 25 | ch := make(chan int) 26 | go waitForMe(ch) 27 | ch <- 64 28 | ch <- 128 29 | close(ch) 30 | wg.Wait() 31 | } 32 | 33 | // EOF OMIT 34 | -------------------------------------------------------------------------------- /code/concurrency102/closed_range.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | var wg sync.WaitGroup 9 | 10 | func waitForMe(ch chan int) { 11 | defer wg.Done() 12 | for value := range ch { 13 | fmt.Println(value) 14 | } 15 | fmt.Println("My channel has closed") 16 | } 17 | 18 | func main() { 19 | wg.Add(1) 20 | ch := make(chan int) 21 | go waitForMe(ch) 22 | ch <- 64 23 | ch <- 128 24 | close(ch) 25 | wg.Wait() 26 | } 27 | 28 | // EOF OMIT 29 | -------------------------------------------------------------------------------- /code/concurrency102/closing-channels-range.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | ch := make(chan string) 7 | 8 | go func(output chan string) { 9 | for i := 0; i < 5; i++ { 10 | output <- fmt.Sprintf("sending N=%d", i) 11 | } 12 | close(output) 13 | }(ch) 14 | 15 | for val := range ch { 16 | fmt.Printf("Recieved: %#v\n", val) 17 | } 18 | 19 | ch <- fmt.Sprintf("where is your towel?") 20 | } 21 | -------------------------------------------------------------------------------- /code/concurrency102/cond.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | type Queue []int 11 | 12 | func (q *Queue) Push(item int) { 13 | *q = append(*q, item) 14 | } 15 | 16 | func (q *Queue) Pop() int { 17 | if !q.IsEmpty() { 18 | item := (*q)[0] 19 | *q = (*q)[1:len(*q)] 20 | return item 21 | } 22 | return 0 23 | } 24 | 25 | func (q *Queue) Len() int { 26 | return len(*q) 27 | } 28 | 29 | func (q *Queue) IsEmpty() bool { 30 | return q.Len() == 0 31 | } 32 | 33 | var ( 34 | cond *sync.Cond = sync.NewCond(new(sync.Mutex)) 35 | workQueue Queue 36 | ) 37 | 38 | func produce() { 39 | i := 0 40 | for { 41 | time.Sleep(time.Duration(rand.Intn(300)) * time.Millisecond) 42 | cond.L.Lock() 43 | workQueue.Push(i) 44 | i++ 45 | cond.Signal() 46 | cond.L.Unlock() 47 | } 48 | } 49 | 50 | func consume(name string) { 51 | for { 52 | cond.L.Lock() 53 | for workQueue.IsEmpty() { 54 | cond.Wait() 55 | fmt.Println(name, "is awoken") 56 | } 57 | item := workQueue.Pop() 58 | cond.L.Unlock() 59 | fmt.Println(name, item) 60 | time.Sleep(time.Duration(rand.Intn(200)+100) * time.Millisecond) 61 | } 62 | } 63 | 64 | func main() { 65 | go consume("First") 66 | go consume("Second") 67 | go consume("Third") 68 | produce() 69 | } 70 | -------------------------------------------------------------------------------- /code/concurrency102/daisy-chain.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // START OMIT 8 | func f(left, right chan int) { 9 | left <- 1 + <-right 10 | } 11 | 12 | func main() { 13 | const n = 100000 14 | leftmost := make(chan int) 15 | right := leftmost 16 | left := leftmost 17 | 18 | for i := 0; i < n; i++ { 19 | right = make(chan int) 20 | go f(left, right) 21 | left = right 22 | } 23 | 24 | go func(c chan int) { c <- 1 }(right) 25 | fmt.Println(<-leftmost) 26 | } 27 | 28 | // END OMIT 29 | -------------------------------------------------------------------------------- /code/concurrency102/deadlock_detection.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | // START OMIT 9 | type Ball struct{ hits int } 10 | 11 | var i int 12 | 13 | func main() { 14 | table := make(chan *Ball) 15 | go player("ping", table) 16 | go player("pong", table) 17 | 18 | table <- new(Ball) // game on; toss the ball 19 | i++ 20 | time.Sleep(1 * time.Second) 21 | <-table 22 | } 23 | 24 | func player(name string, table chan *Ball) { 25 | for { 26 | i++ 27 | ball := <-table 28 | ball.hits++ 29 | fmt.Println(name, ball.hits) 30 | time.Sleep(100 * time.Millisecond) 31 | table <- ball 32 | } 33 | } 34 | // END OMIT 35 | -------------------------------------------------------------------------------- /code/concurrency102/dummy_fanin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func talk(msg string) <-chan string { // HL 10 | c := make(chan string) 11 | go func() { 12 | for i := 0; ; i++ { 13 | c <- fmt.Sprintf("%s: %d", msg, i) 14 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 15 | } 16 | }() 17 | return c 18 | } 19 | 20 | func main() { 21 | doycho := talk("Doycho") // HL 22 | misho := talk("Misho") // HL 23 | for i := 0; i < 5; i++ { 24 | fmt.Println(<-doycho) 25 | fmt.Println(<-misho) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /code/concurrency102/error_demo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | ) 7 | 8 | func main() { 9 | if contents, err := ioutil.ReadFile("/etc/hosts"); err != nil { 10 | fmt.Println(err.Error()) 11 | fmt.Printf("%#v\n", err) 12 | } else { 13 | fmt.Printf("%s", contents) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /code/concurrency102/fib.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func fib() <-chan int { 4 | c := make(chan int) 5 | go func() { 6 | for a, b := 0, 1; ; a, b = b, a+b { 7 | c <- a 8 | } 9 | }() 10 | return c 11 | } 12 | 13 | func main() { 14 | fibonacci := fib() 15 | for i := 0; i < 10; i++ { 16 | println(<-fibonacci) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /code/concurrency102/go-less-boring-sleep.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | boring := func(msg string) { 11 | for i := 0; ; i++ { 12 | fmt.Println(msg, i) 13 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 14 | } 15 | } 16 | 17 | go boring("boring!") 18 | fmt.Println("Listening.") 19 | time.Sleep(2 * time.Second) // HL 20 | fmt.Println("You are way too boring. I am leaving.") 21 | } 22 | -------------------------------------------------------------------------------- /code/concurrency102/indeterminate-select.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | import "time" 5 | 6 | func main() { 7 | c := make(chan int) 8 | 9 | go func() { 10 | c <- 42 11 | }() 12 | 13 | select { 14 | case v1 := <-c: 15 | fmt.Printf("received %d in v1\n", v1) 16 | case v2 := <-c: 17 | fmt.Printf("received %d in v2\n", v2) 18 | case <-time.After(1 * time.Nanosecond): 19 | fmt.Printf("timeout\n") 20 | default: 21 | fmt.Printf("nothing!\n") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /code/concurrency102/muffin-answer-func-chan-methods.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type queue chan string 6 | type callableStriner func() string 7 | 8 | func (q queue) add(s string) { 9 | q <- s 10 | } 11 | func (q queue) poll() string { 12 | return <-q 13 | } 14 | func (cs callableStriner) String() string { 15 | return cs() 16 | } 17 | 18 | func main() { 19 | q := make(queue, 10) 20 | q.add("test") 21 | fmt.Printf("%s\n", callableStriner(q.poll)) 22 | } 23 | -------------------------------------------------------------------------------- /code/concurrency102/mutex.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func main() { 9 | var ( 10 | count int 11 | countMtx sync.Mutex // HL 12 | countWg sync.WaitGroup 13 | ) 14 | 15 | worker := func() { 16 | countMtx.Lock() // HL 17 | count += 1 18 | countMtx.Unlock() // HL 19 | countWg.Done() 20 | } 21 | 22 | for i := 0; i < 8; i++ { 23 | countWg.Add(1) 24 | go worker() 25 | } 26 | 27 | countWg.Wait() 28 | fmt.Println("counter:", count) 29 | } 30 | -------------------------------------------------------------------------------- /code/concurrency102/mutex_chan.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func main() { 9 | var ( 10 | count int 11 | countWg sync.WaitGroup 12 | ) 13 | ch := make(chan struct{}, 1) // HL 14 | 15 | worker := func() { 16 | ch <- struct{}{} // HL 17 | count += 1 18 | <-ch // HL 19 | countWg.Done() 20 | } 21 | 22 | for i := 0; i < 8; i++ { 23 | countWg.Add(1) 24 | go worker() 25 | } 26 | 27 | countWg.Wait() 28 | fmt.Println("counter:", count) 29 | } 30 | -------------------------------------------------------------------------------- /code/concurrency102/once.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func main() { 9 | var once sync.Once // HL 10 | var wg sync.WaitGroup 11 | 12 | onceBody := func() { 13 | fmt.Println("Only once") 14 | } 15 | anotherBody := func() { 16 | fmt.Println("Another") 17 | } 18 | 19 | for i := 0; i < 10; i++ { 20 | wg.Add(1) 21 | go func() { 22 | once.Do(onceBody) // HL 23 | once.Do(anotherBody) // HL 24 | wg.Done() 25 | }() 26 | } 27 | wg.Wait() 28 | } 29 | -------------------------------------------------------------------------------- /code/concurrency102/pipes.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char *argv[]) { 6 | char *const cat_args[] = {"cat", argv[1], NULL}; 7 | char *const wc_args[] = {"wc", "-l", NULL}; 8 | 9 | if (fork() == 0) { 10 | int pipefd[2]; 11 | pipe(pipefd); 12 | 13 | if (fork() == 0) { 14 | // replace standard input with input part of pipe 15 | dup2(pipefd[0], 0); 16 | // close unused hald of pipe 17 | close(pipefd[1]); 18 | // execute wc 19 | execvp("wc", wc_args); 20 | } else { 21 | // replace standard output with output part of pipe 22 | dup2(pipefd[1], 1); 23 | // close unused unput half of pipe 24 | close(pipefd[0]); 25 | // execute cat 26 | execvp("cat", cat_args); 27 | } 28 | } else { 29 | wait(NULL); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /code/concurrency102/select_fanin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | // TALK START OMIT 10 | func talk(msg string) <-chan string { // HL 11 | c := make(chan string) 12 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 13 | go func() { 14 | for i := 0; ; i++ { 15 | c <- fmt.Sprintf("%s: %d", msg, i) 16 | time.Sleep(time.Duration(r.Intn(1e3)) * time.Millisecond) 17 | } 18 | }() 19 | return c 20 | } 21 | 22 | // TALK END OMIT 23 | 24 | // FANIN START OMIT 25 | func fanIn(input1, input2 <-chan string) <-chan string { // HL 26 | c := make(chan string) 27 | go func() { // HL 28 | for { 29 | select { // HL 30 | case s := <-input1: 31 | c <- s // HL 32 | case s := <-input2: 33 | c <- s // HL 34 | } // HL 35 | } 36 | }() 37 | return c 38 | } 39 | 40 | // FANIN END OMIT 41 | // MAIN START OMIT 42 | func main() { 43 | c := fanIn(talk("Misho"), talk("Doycho")) // HL 44 | for i := 0; i < 10; i++ { 45 | fmt.Println(<-c) // HL 46 | } 47 | } 48 | 49 | // MAIN END OMIT 50 | -------------------------------------------------------------------------------- /code/concurrency102/select_finish_fanin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | var wg sync.WaitGroup 11 | 12 | // TALK START OMIT 13 | func talk(msg string) <-chan string { // HL 14 | c := make(chan string) 15 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 16 | go func() { 17 | for i := 0; ; i++ { 18 | c <- fmt.Sprintf("%s: %d", msg, i) 19 | time.Sleep(time.Duration(r.Intn(1e3)) * time.Millisecond) 20 | } 21 | }() 22 | return c 23 | } 24 | 25 | // TALK END OMIT 26 | 27 | // FANIN START OMIT 28 | func fanIn(input1, input2 <-chan string, finish chan struct{}) <-chan string { // HL 29 | c := make(chan string) 30 | go func() { 31 | for { 32 | select { 33 | case s := <-input1: 34 | c <- s 35 | case s := <-input2: 36 | c <- s 37 | case <-finish: // HL 38 | close(c) // HL 39 | wg.Done() 40 | return 41 | } 42 | } 43 | }() 44 | return c 45 | } 46 | 47 | // FANIN END OMIT 48 | 49 | // MAIN START OMIT 50 | func main() { 51 | wg.Add(1) 52 | finish := make(chan struct{}) // HL 53 | c := fanIn(talk("Doycho"), talk("Misho"), finish) // HL 54 | for value := range c { 55 | fmt.Println(value) 56 | if len(value) > 9 { 57 | close(finish) 58 | } 59 | } 60 | wg.Wait() 61 | } 62 | 63 | // MAIN END OMIT 64 | -------------------------------------------------------------------------------- /code/concurrency102/timeout.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "time" 7 | ) 8 | 9 | func fetch(url string) <-chan *http.Response { 10 | ch := make(chan *http.Response) 11 | 12 | go func() { 13 | if response, err := http.Get(url); err == nil { 14 | ch <- response 15 | } 16 | }() 17 | 18 | return ch 19 | } 20 | 21 | func main() { 22 | select { 23 | case resp := <-fetch("https://www.google.com/search?q=golang"): 24 | fmt.Printf("received %v from google\n", resp.Status) 25 | case resp := <-fetch("https://www.bing.com/search?q=golang"): 26 | fmt.Printf("received %v from bing\n", resp.Status) 27 | case <-time.After(500 * time.Millisecond): 28 | fmt.Printf("timed out\n") 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /code/concurrency102/waitgroup.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "sync" 7 | ) 8 | 9 | func main() { 10 | var wg sync.WaitGroup 11 | var urls = []string{ 12 | "http://www.golang.org/", 13 | "http://www.google.com/", 14 | "http://does-not-exists-for-real.com/", 15 | } 16 | for _, url := range urls { 17 | wg.Add(1) // Increment the WaitGroup counter. 18 | // Launch a goroutine to fetch the URL. 19 | go func(url string) { 20 | content, err := http.Get(url) // Fetch the URL. 21 | if err == nil { 22 | fmt.Println(url, content.Status) 23 | } else { 24 | fmt.Println(url, "has failed") 25 | } 26 | wg.Done() // Decrement the counter when the goroutine completes. 27 | }(url) 28 | } 29 | // Wait for all HTTP fetches to complete. 30 | wg.Wait() 31 | } 32 | -------------------------------------------------------------------------------- /code/course-info/hello_http.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "html" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | ) 10 | 11 | func main() { 12 | go http.ListenAndServe(":8282", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 13 | fmt.Fprintf(w, "Hello, %s", html.EscapeString(r.URL.Path)) 14 | })) 15 | 16 | res, err := http.Get("http://localhost:8282/world") 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | defer res.Body.Close() 21 | 22 | contents, err := ioutil.ReadAll(res.Body) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | fmt.Printf("Server responded with: %s", contents) 27 | } 28 | -------------------------------------------------------------------------------- /code/course-info/hello_world.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println("Hello, world!") 7 | } 8 | -------------------------------------------------------------------------------- /code/data_structures/array.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | var x [5]float64 7 | x[0] = 98 8 | x[1] = 93 9 | x[2] = 77 10 | x[3] = 82 11 | x[4] = 83 12 | 13 | var total float64 = 0 14 | for i := 0; i < 5; i++ { 15 | total += x[i] 16 | } 17 | fmt.Println(total / 5) 18 | } 19 | -------------------------------------------------------------------------------- /code/data_structures/array_comparison.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | dqdo := [4]uint32{0, 1, 2, 3} 7 | baba := [4]uint32{0, 1, 2, 3} 8 | 9 | fmt.Printf("The two arrays are identical: %t\n", dqdo == baba) 10 | } 11 | -------------------------------------------------------------------------------- /code/data_structures/array_comparison_error.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | dqdo := [4]uint32{0, 1, 2, 3} 7 | baba := [5]uint32{0, 1, 2, 3} // HL 8 | 9 | fmt.Printf("The two arrays are identical: %t\n", dqdo == baba) 10 | } 11 | -------------------------------------------------------------------------------- /code/data_structures/len_vs_cap.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | x := []int{2, 3, 5, 7, 11} 7 | y := x[1:3] 8 | 9 | fmt.Println("len(x) =", len(x), ", cap(x) =", cap(x)) 10 | fmt.Println("len(y) =", len(y), ", cap(y) =", cap(y)) 11 | } 12 | -------------------------------------------------------------------------------- /code/data_structures/new_vs_make.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type example struct { 6 | attrs map[string]int 7 | } 8 | 9 | func main() { 10 | e := new(example) 11 | e.attrs = make(map[string]int) 12 | e.attrs["h"] = 42 13 | fmt.Println(e) 14 | } 15 | -------------------------------------------------------------------------------- /code/data_structures/slices_copy_examples.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | var l int 7 | slice1 := []int{1, 2, 3, 4} 8 | slice2 := []int{7, 6, 5} 9 | arr := [3]int{1, 1, 1} 10 | 11 | // Копираме трите елемента от `slice2` в `slice1` 12 | l = copy(slice1, slice2) // slice1 = [7 6 5 4], l = 3 13 | fmt.Printf("slice1 = %v, l = %v\n", slice1, l) 14 | 15 | // Копираме края на slice1 в началото му 16 | l = copy(slice1, slice1[2:]) // slice1 = [5 4 5 4], l = 2 17 | fmt.Printf("slice1 = %v, l = %v\n", slice1, l) 18 | 19 | // Копираме slice1 в slice2 20 | l = copy(slice2, slice1) // slice2 = [5 4 5], l = 3 21 | // Копират се само първите 3 елемента, защото len(slice2) = 3 22 | fmt.Printf("slice2 = %v, l = %v\n", slice2, l) 23 | 24 | } 25 | -------------------------------------------------------------------------------- /code/errors_and_testing/c_err_example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | extern int errno; 6 | 7 | int main () 8 | { 9 | FILE* pf = fopen("unexist.txt", "rb"); 10 | if (pf == NULL) { 11 | fprintf(stderr, "Value of errno: %d\n", errno); 12 | perror("Error printed by perror"); 13 | fprintf(stderr, "Error opening file: %s\n", strerror(errno)); 14 | } 15 | else { 16 | fclose(pf); 17 | } 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /code/errors_and_testing/defer_example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println("start") 7 | defer fmt.Println("first") 8 | 9 | if false { // ex. try to open a file 10 | fmt.Println("error") 11 | return 12 | } 13 | defer fmt.Println("second") 14 | 15 | fmt.Println("done") 16 | } 17 | -------------------------------------------------------------------------------- /code/errors_and_testing/muffin-q3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | import "time" 5 | 6 | func main() { 7 | c := make(chan int) 8 | 9 | go func() { 10 | c <- 42 11 | }() 12 | 13 | select { 14 | case v1 := <-c: 15 | fmt.Printf("received %d in v1\n", v1) 16 | case v2 := <-c: 17 | fmt.Printf("received %d in v2\n", v2) 18 | case <-time.After(1 * time.Nanosecond): 19 | fmt.Printf("timeout\n") 20 | default: 21 | fmt.Printf("nothing!\n") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /code/errors_and_testing/panic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | f() 7 | fmt.Println("Returned normally from f.") 8 | } 9 | 10 | func f() { 11 | defer func() { 12 | if r := recover(); r != nil { 13 | fmt.Println("Recovered in f", r) 14 | } 15 | }() 16 | fmt.Println("Calling g.") 17 | g(0) 18 | fmt.Println("Returned normally from g.") 19 | } 20 | func g(i int) { 21 | if i > 2 { 22 | fmt.Println("Panicking!") 23 | panic(fmt.Sprintf("%v", i)) 24 | } 25 | defer fmt.Println("Defer in g", i) 26 | fmt.Println("Printing in g", i) 27 | g(i + 1) 28 | } 29 | 30 | //END OMIT 31 | -------------------------------------------------------------------------------- /code/errors_and_testing/table_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func Fibonacci(i int) int { 10 | return 42 / 2 11 | } 12 | 13 | // TABLE TEST START 14 | func TestFibonacci(t *testing.T) { 15 | var tests = []struct { 16 | input, result int 17 | }{ 18 | {input: 2, result: 1}, 19 | {input: 8, result: 21}, 20 | {input: 10, result: 55}, 21 | } 22 | for _, test := range tests { 23 | testingFunc(t, test.input, test.result) 24 | } 25 | } 26 | 27 | // TABLE TEST OMIT 28 | 29 | // SUB TEST START 30 | func TestSubFibonacci(t *testing.T) { 31 | var tests = []struct { 32 | input, result int 33 | }{ 34 | {input: 2, result: 1}, 35 | {input: 8, result: 21}, 36 | {input: 10, result: 55}, 37 | } 38 | for _, test := range tests { 39 | t.Run(fmt.Sprintf("Fibonacci(%d)", test.input), func(t *testing.T) { 40 | testingFunc(t, test.input, test.result) 41 | }) 42 | } 43 | } 44 | 45 | // SUB TEST OMIT 46 | 47 | // SUB BENCHMARK START 48 | func BenchmarkSubFibonacci(b *testing.B) { 49 | var tests = []struct { 50 | input, result int 51 | }{ 52 | {input: 2, result: 1}, 53 | {input: 8, result: 21}, 54 | {input: 10, result: 55}, 55 | } 56 | for _, test := range tests { 57 | b.Run(fmt.Sprintf("BFibonacci(%d)", test.input), func(b *testing.B) { 58 | for i := 0; i < b.N; i++ { 59 | testingFunc(b, test.input, test.result) 60 | } 61 | }) 62 | } 63 | } 64 | 65 | // SUB BENCHMARK OMIT 66 | 67 | func testingFunc(tb testing.TB, input, expectedResult int) { 68 | if result := Fibonacci(input); result != expectedResult { 69 | tb.Fatalf("Expected %d for Fiboncci(%d) but got %d", expectedResult, input, result) 70 | } 71 | } 72 | 73 | // END testingFunc OMIT 74 | 75 | var tests = []struct { 76 | input, result int 77 | }{ 78 | {input: 2, result: 1}, 79 | {input: 8, result: 21}, 80 | {input: 10, result: 55}, 81 | } 82 | 83 | // SUB GROUP START 84 | func TestGroupSubFibonacci(t *testing.T) { 85 | t.Run("group1", func(t *testing.T) { 86 | for _, test := range tests { 87 | t.Run(fmt.Sprintf("Fibonacci(%d)", test.input), func(t *testing.T) { 88 | var input, result = test.input, test.result 89 | t.Parallel() 90 | time.Sleep(time.Second) 91 | testingFunc(t, input, result) 92 | }) 93 | 94 | } 95 | t.Run("NonParallel", func(t *testing.T) { 96 | t.Fatal("Just cause") 97 | }) 98 | t.Fatal("Oops") 99 | t.Run("NonParallel2", func(t *testing.T) { 100 | t.Fatal("Just Cause II") 101 | }) 102 | }) 103 | } 104 | 105 | // SUB GROUP OMIT 106 | -------------------------------------------------------------------------------- /code/errors_and_testing/testing.go: -------------------------------------------------------------------------------- 1 | /* 2 | Example fobonacci numbers implementation 3 | */ 4 | package fibonacci 5 | 6 | import ( 7 | "fmt" 8 | "testing" 9 | ) 10 | 11 | // lookupTable stores computed results from FibonacciFast or FibonacciFastest. 12 | var lookupTable = map[uint64]uint64{} 13 | 14 | // Fastest Fibonacci Implementation 15 | func FibonacciFastest(n uint64) uint64 { 16 | return 42 17 | } 18 | 19 | func BenchmarkFibonacciFastest(b *testing.B) { 20 | for i := 0; i < b.N; i++ { 21 | FibonacciFastest(40) 22 | } 23 | } 24 | 25 | // END BENCH OMIT 26 | 27 | func TestFibonacciFastest(t *testing.T) { 28 | n := FibonacciFastest(0) 29 | if n != 1 { 30 | t.Error("FibonnaciFastest(0) returned" + n + ", we expected 1") 31 | } 32 | } 33 | 34 | // END TEST OMIT 35 | 36 | func Hello() { 37 | fmt.Println("hello") 38 | } 39 | 40 | func ExampleHello() { 41 | Hello("hello") 42 | // Output: 43 | // hello 44 | } 45 | 46 | // END EXAMPLE OMIT 47 | -------------------------------------------------------------------------------- /code/funcs_and_pointers/as_values.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func foo(bar func(int, float64) float64) float64 { 8 | return bar(5, 3.2) 9 | } 10 | 11 | func createRandomGenerator() func() int { 12 | return func() int { 13 | return 4 14 | } 15 | } 16 | 17 | func main() { 18 | fmt.Println(createRandomGenerator()()) 19 | } 20 | -------------------------------------------------------------------------------- /code/funcs_and_pointers/broken_pointer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var p *int = nil 5 | *p = 0 6 | } 7 | -------------------------------------------------------------------------------- /code/funcs_and_pointers/defer_example1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func deferExample() { 8 | for i := 0; i < 5; i++ { 9 | defer func(i int) { 10 | fmt.Printf(" %v", i) 11 | }(i) 12 | } 13 | } 14 | 15 | func main() { 16 | deferExample() 17 | } 18 | -------------------------------------------------------------------------------- /code/funcs_and_pointers/defer_example2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func deferExample() { 8 | for i := 0; i < 5; i++ { 9 | defer func() { 10 | fmt.Printf(" %v", i) 11 | }() 12 | } 13 | } 14 | 15 | func main() { 16 | deferExample() 17 | } 18 | -------------------------------------------------------------------------------- /code/funcs_and_pointers/defer_example_0.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println("counting") 7 | 8 | for i := 0; i < 10; i++ { 9 | defer fmt.Println(i) 10 | } 11 | 12 | fmt.Println("done") 13 | } 14 | -------------------------------------------------------------------------------- /code/funcs_and_pointers/factorial.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println("factorial(5) returns:") 7 | fmt.Println(factorial(5)) 8 | } 9 | 10 | func factorial(x uint) uint { 11 | if x == 0 { 12 | return 1 13 | } 14 | 15 | return x * factorial(x-1) 16 | } 17 | -------------------------------------------------------------------------------- /code/funcs_and_pointers/lambdas.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | add := func(x, y int) int { return x + y } 7 | fmt.Println(add(4, 5)) 8 | 9 | fmt.Println(func(x, y int) int { return x + y }(3, 8)) 10 | } 11 | -------------------------------------------------------------------------------- /code/funcs_and_pointers/lots_of_arguments.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println(sum(2, 3, 4, 5)) 7 | sum() //0 8 | sum(2, 3) //5 9 | sum(2, 3, 4, 5) //14 10 | } 11 | 12 | func sum(args ...int) int { 13 | result := 0 14 | for _, v := range args { 15 | result += v 16 | } 17 | 18 | return result 19 | } 20 | -------------------------------------------------------------------------------- /code/funcs_and_pointers/many_results.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | sum, count := sumAndCount(2, 3, 4, 5) 7 | fmt.Println("Резултатът от sumAndCount() е", sum, count) 8 | } 9 | 10 | func sumAndCount(args ...int) (int, int) { 11 | result := 0 12 | for _, v := range args { 13 | result += v 14 | } 15 | 16 | return result, len(args) 17 | } 18 | -------------------------------------------------------------------------------- /code/funcs_and_pointers/named_sum_and_count.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println(sumAndCount(2, 3, 4, 5)) 7 | } 8 | 9 | func sumAndCount(args ...int) (result int, count int) { 10 | count = len(args) 11 | for _, v := range args { 12 | result += v 13 | } 14 | 15 | return 16 | } 17 | -------------------------------------------------------------------------------- /code/funcs_and_pointers/pointers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | var ( 6 | name string = "Чочко" 7 | age uint8 = 27 8 | pName *string 9 | ) 10 | 11 | func main() { 12 | pName = &name 13 | fmt.Printf("name е на адрес %p и има стойност %s\n", pName, name) 14 | fmt.Printf("age е на адрес %p и има стойност %d\n", &age, age) 15 | } 16 | -------------------------------------------------------------------------------- /code/funcs_and_pointers/simple_funcs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func ping() { 9 | fmt.Println("pong") 10 | } 11 | 12 | func myprint(s string) { 13 | fmt.Println(s) 14 | } 15 | 16 | func shout(s string) string { 17 | return strings.ToUpper(s) + "!!!1!" 18 | } 19 | 20 | func main() { 21 | ping() 22 | myprint("woot") 23 | myprint(shout("woot")) 24 | } 25 | -------------------------------------------------------------------------------- /code/funcs_and_pointers/visibility.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func counter(start int) func() int { 6 | count := start 7 | return func() int { 8 | count++ 9 | return count 10 | } 11 | } 12 | 13 | func main() { 14 | c := counter(3) 15 | fmt.Println("The first 3 values are", c(), c(), c()) 16 | } 17 | -------------------------------------------------------------------------------- /code/goruntime/easter_eggs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | notwithstanding var a int = 10 7 | whereas var b int = 20 8 | if despiteallobjections a < b && insofaras a != 3 { 9 | fmt.Printf("hello, world\n") 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /code/goruntime/esc.go: -------------------------------------------------------------------------------- 1 | package optimizations 2 | 3 | type Cursor struct { 4 | X, Y int 5 | } 6 | 7 | const width, height = 640, 480 8 | 9 | func Center(c *Cursor) { 10 | c.X += width / 2 11 | c.Y += height / 2 12 | } 13 | 14 | func CenterCursor() (int, int) { 15 | c := new(Cursor) 16 | Center(c) 17 | return c.X, c.Y 18 | } 19 | 20 | // BEGIN Sum OMIT 21 | func SumFirst100() int { 22 | numbers := make([]int, 100) 23 | for i := range numbers { 24 | numbers[i] = i + 1 25 | } 26 | var sum int 27 | for _, i := range numbers { 28 | sum += i 29 | } 30 | return sum 31 | } 32 | 33 | // END Sum OMIT 34 | -------------------------------------------------------------------------------- /code/intro/endless_for.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | for { 7 | fmt.Println("Can't stop me!") 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /code/intro/hello_world.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Printf("Hello, world!\n") 7 | } 8 | -------------------------------------------------------------------------------- /code/intro/hello_world_with_useless_import.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | fmt.Printf("Hello, world!\n") 10 | } 11 | -------------------------------------------------------------------------------- /code/intro/implicit_cast_error.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | number := 42 5 | specificNumber := float64(number) 6 | println(number * specificNumber) // (mismatched types int and float64) 7 | } 8 | -------------------------------------------------------------------------------- /code/intro/init.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | var subject string 6 | 7 | func init() { 8 | subject = "world" 9 | } 10 | 11 | func main() { 12 | fmt.Printf("Hello, %s!\n", subject) 13 | } 14 | -------------------------------------------------------------------------------- /code/intro/iota.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | const ( 6 | B uint64 = iota + 1 7 | KB = 1 << (10 * iota) 8 | MB 9 | GB 10 | TB 11 | ) 12 | 13 | func main() { 14 | fmt.Printf("Result is %d bytes", 2*KB) 15 | } 16 | -------------------------------------------------------------------------------- /code/intro/strings_and_runes.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Printf("We have a cool fmt package: %c %c!\n", '\u2713', '☢') 7 | fmt.Printf("こんにちは世界!\n") 8 | fmt.Printf(`With backticks we can ignore backslashes like \n!`) 9 | } 10 | -------------------------------------------------------------------------------- /code/intro/unused_var.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | name := "Кевин" 7 | fmt.Printf("Май забравихме някой?\n") 8 | } 9 | -------------------------------------------------------------------------------- /code/intro/wrong_type_inference.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | name := "Киро" 6 | 7 | func main() { 8 | fmt.Printf("%s, защо да не може?\n", name) 9 | } 10 | -------------------------------------------------------------------------------- /code/types_and_interfaces/anon_structs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // start OMIT 6 | type Person struct { 7 | firstName, lastName string 8 | } 9 | 10 | func (p Person) Name() string { 11 | return p.firstName + " " + p.lastName 12 | } 13 | 14 | type Student struct { 15 | Person 16 | facultyNumber int16 17 | } 18 | 19 | func main() { 20 | s := Student{Person{"Иван", "Иванов"}, 123} 21 | fmt.Printf("We have a student with name %s and FN %d", s.Name(), s.facultyNumber) 22 | } 23 | 24 | // end OMIT 25 | -------------------------------------------------------------------------------- /code/types_and_interfaces/bound_methods.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type greeter struct { 6 | Name string 7 | } 8 | 9 | func (g *greeter) SayHi(name string) { 10 | fmt.Printf("Hi, %s! I am %s (%p)\n", name, g.Name, g) 11 | } 12 | 13 | func main() { 14 | var p = &greeter{Name: "p"} 15 | pGreeter := p.SayHi 16 | 17 | fmt.Printf("Calling p.SayHi:\n") 18 | p.SayHi("Pesho") 19 | 20 | fmt.Printf("Calling pGreeter:\n") 21 | pGreeter("Pesho") // The same as p.SayHi("Pesho") 22 | 23 | unboundGreeter := (*greeter).SayHi 24 | fmt.Printf("Calling unboundGreeter:\n") 25 | unboundGreeter(p, "Gosho") 26 | 27 | unboundGreeter(&greeter{Name: "other"}, "Maria") 28 | } 29 | -------------------------------------------------------------------------------- /code/types_and_interfaces/custom_json.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type Triangle struct { 9 | X, Y, Z int 10 | } 11 | 12 | // start OMIT 13 | func (t *Triangle) MarshalJSON() ([]byte, error) { 14 | return json.Marshal(struct { 15 | X, Y, Z int 16 | Shape string 17 | }{ 18 | X: t.X, 19 | Y: t.Y, 20 | Z: t.Z, 21 | Shape: "Триъгълник", 22 | }) 23 | } 24 | 25 | func main() { 26 | tr := &Triangle{X: 12, Y: 64, Z: 50} 27 | marshalledTr, _ := json.Marshal(tr) 28 | fmt.Printf("%s\n", marshalledTr) 29 | } 30 | 31 | // end OMIT 32 | -------------------------------------------------------------------------------- /code/types_and_interfaces/integer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type integer int 6 | 7 | func (i integer) Abs() integer { 8 | switch { 9 | case i < 0: 10 | return -i 11 | case i == 0: 12 | return 0 13 | default: 14 | return i 15 | } 16 | } 17 | 18 | func (i *integer) Increment() { 19 | *i++ 20 | } 21 | 22 | func main() { 23 | var number integer 24 | number = -42 25 | 26 | number.Increment() 27 | fmt.Println(number.Abs()) 28 | } 29 | 30 | // end OMIT 31 | -------------------------------------------------------------------------------- /code/types_and_interfaces/interface_conversions.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type mystruct struct{ a, b int } 6 | 7 | func (m mystruct) String() string { 8 | return fmt.Sprintf("mystruct(%d, %d) yo!", m.a, m.b) 9 | } 10 | 11 | func stringify(value interface{}) string { 12 | switch s := value.(type) { 13 | case string: 14 | return s 15 | case fmt.Stringer: 16 | return s.String() 17 | case int, uint: 18 | return fmt.Sprintf("%06d", s) // leftpad 19 | default: 20 | return "No clue, mate :)" 21 | } 22 | } 23 | 24 | func main() { 25 | fmt.Printf("%#v\n", stringify(mystruct{1, 2})) 26 | } 27 | -------------------------------------------------------------------------------- /code/types_and_interfaces/nil_objects.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type integer int 6 | 7 | func (i *integer) Increment() { 8 | fmt.Println("Incrementing...") 9 | *i++ 10 | } 11 | 12 | func main() { 13 | var number *integer 14 | number.Increment() 15 | fmt.Println(number) 16 | } 17 | 18 | // end OMIT 19 | -------------------------------------------------------------------------------- /code/types_and_interfaces/plain_json.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type Rectangle struct { 9 | X, Y int 10 | } 11 | 12 | func main() { 13 | // start OMIT 14 | var empty interface{} 15 | emptyRect := new(Rectangle) 16 | 17 | rect := &Rectangle{X: 12, Y: 64} 18 | 19 | marshalledRect, _ := json.Marshal(rect) 20 | fmt.Printf("%s\n", marshalledRect) 21 | 22 | json.Unmarshal(marshalledRect, emptyRect) 23 | fmt.Printf("%#v\n", emptyRect) 24 | 25 | json.Unmarshal(marshalledRect, &empty) 26 | fmt.Printf("%#v\n", empty) 27 | // end OMIT 28 | } 29 | -------------------------------------------------------------------------------- /code/types_and_interfaces/shapes.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | // start interface OMIT 9 | type Shape interface { 10 | Area() float64 11 | Circumference() int 12 | } 13 | 14 | // end interface OMIT 15 | 16 | // start types OMIT 17 | type Rectangle struct { 18 | X, Y int 19 | } 20 | 21 | type Triangle struct { 22 | X, Y, Z int 23 | } 24 | 25 | // end types OMIT 26 | 27 | // start methods OMIT 28 | func (r *Rectangle) Area() float64 { 29 | return float64(r.X * r.Y) 30 | } 31 | 32 | func (r *Rectangle) Circumference() int { 33 | return 2 * (r.X + r.Y) 34 | } 35 | 36 | func (t *Triangle) Area() float64 { 37 | p := float64(t.Circumference() / 2) 38 | return math.Sqrt(p * (p - float64(t.X)) * (p - float64(t.Y)) * (p - float64(t.Z))) 39 | } 40 | 41 | func (t *Triangle) Circumference() int { 42 | return t.X + t.Y + t.Z 43 | } 44 | 45 | // end methods OMIT 46 | 47 | // start funcs OMIT 48 | func sumOfCircumferences(shapes ...Shape) int { 49 | sum := 0 50 | for _, shape := range shapes { 51 | sum += shape.Circumference() 52 | } 53 | return sum 54 | } 55 | 56 | func biggestArea(shapes ...Shape) (result float64) { 57 | for _, shape := range shapes { 58 | area := shape.Area() 59 | if area > result { 60 | result = area 61 | } 62 | } 63 | return result 64 | } 65 | 66 | func main() { 67 | rect := &Rectangle{X: 12, Y: 64} 68 | tr := &Triangle{X: 12, Y: 64, Z: 50} 69 | fmt.Println(sumOfCircumferences(rect, tr)) 70 | fmt.Println(biggestArea(rect, tr)) 71 | } 72 | 73 | // end funcs OMIT 74 | -------------------------------------------------------------------------------- /code/types_and_interfaces/stringer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | // start OMIT 9 | type myint uint64 10 | 11 | func (i myint) String() string { 12 | return "myint in binary is " + strconv.FormatUint(uint64(i), 2) 13 | } 14 | 15 | func main() { 16 | var i myint 17 | i = 5 18 | fmt.Printf("Value: %s\n", i) 19 | } 20 | 21 | // end OMIT 22 | -------------------------------------------------------------------------------- /code/types_and_interfaces/type_aliases.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // START OMIT 4 | type integer int 5 | 6 | func isPositive(a integer) bool { 7 | return a > 0 8 | } 9 | 10 | type integerAlias = int 11 | 12 | func isPositive1(a integerAlias) bool { 13 | return a > 0 14 | } 15 | 16 | func main() { 17 | a := 5 18 | isPositive(a) // cannot use a (type int) as type integer in argument to isPositive 19 | isPositive1(a) 20 | } 21 | 22 | // END OMIT 23 | -------------------------------------------------------------------------------- /code/types_and_interfaces/type_assertion.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | var value interface{} 7 | value = 20 8 | value = "asd" 9 | 10 | if str, ok := value.(string); ok { 11 | fmt.Printf("We have a %T: %v\n", str, str) 12 | } else { 13 | fmt.Println("Oops, not a string") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /code/web-programming/file-server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | var ( 10 | ListenAddress string 11 | ) 12 | 13 | func init() { 14 | flag.StringVar(&ListenAddress, "addr", ":8080", "Listening address and port.") 15 | } 16 | 17 | func main() { 18 | flag.Parse() 19 | 20 | directory := "." 21 | if len(flag.Args()) >= 1 { 22 | directory = flag.Arg(0) 23 | } 24 | 25 | serverHandler := http.FileServer(http.Dir(directory)) 26 | log.Fatal(http.ListenAndServe(ListenAddress, serverHandler)) 27 | } 28 | -------------------------------------------------------------------------------- /code/web-programming/handlers-mux.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "net/http/httputil" 8 | "net/url" 9 | "time" 10 | ) 11 | 12 | func main() { 13 | mux := http.NewServeMux() 14 | mux.Handle("/doc/", http.FileServer(http.Dir("/usr/share/"))) 15 | mux.Handle("/google/", http.RedirectHandler("https://www.google.com", http.StatusTemporaryRedirect)) 16 | mux.Handle("/proxy/", http.StripPrefix("/proxy/", httputil.NewSingleHostReverseProxy( 17 | &url.URL{Scheme: "https", Host: "mirrors.kernel.org", Path: "/"}, 18 | ))) 19 | 20 | mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { 21 | if req.URL.Path == "/" { 22 | fmt.Fprintf(w, "Welcome to the jungle!") 23 | return 24 | } 25 | http.NotFound(w, req) 26 | }) 27 | 28 | s := &http.Server{Addr: ":8282", Handler: mux, WriteTimeout: 1 * time.Second} 29 | log.Printf("Starting server on %s", s.Addr) 30 | log.Fatal(s.ListenAndServe()) 31 | } 32 | -------------------------------------------------------------------------------- /code/web-programming/http-client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" // HL 6 | ) 7 | 8 | func main() { 9 | resp, err := http.Get("http://fmi.golang.bg/") // HL 10 | if err != nil { 11 | fmt.Println(err) 12 | return 13 | } 14 | fmt.Printf("GET fmi golang site said HTTP %d\n", resp.StatusCode) 15 | } 16 | -------------------------------------------------------------------------------- /code/web-programming/http-server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "html" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | ) 10 | 11 | func main() { 12 | go http.ListenAndServe(":8282", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 13 | fmt.Fprintf(w, "Hello, %s", html.EscapeString(r.URL.Path)) 14 | })) 15 | 16 | res, err := http.Get("http://localhost:8282/world") 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | contents, err := ioutil.ReadAll(res.Body) 21 | res.Body.Close() 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | fmt.Printf("Server responded with: %s", contents) 26 | } 27 | -------------------------------------------------------------------------------- /index.yml: -------------------------------------------------------------------------------- 1 | 0: 2 | title: 00. Нулева лекция 3 | date: 2018-10-03 4 | url: https://lectures.fmi.golang.bg/00-course-info.slide 5 | 1: 6 | title: 01. Въведение в Go 7 | date: 2018-10-10 8 | url: https://lectures.fmi.golang.bg/01-intro.slide 9 | 2: 10 | title: 02. Функции и указатели 11 | date: 2018-10-17 12 | url: https://lectures.fmi.golang.bg/02-funcs_and_pointers.slide 13 | 3: 14 | title: 03. Структури от данни 15 | date: 2018-10-24 16 | url: https://lectures.fmi.golang.bg/03-data_structures.slide 17 | 4: 18 | title: 04. Типове и интерфейси 19 | date: 2018-10-31 20 | url: https://lectures.fmi.golang.bg/04-types_and_interfaces.slide 21 | 5: 22 | title: 05. Concurrency 101 23 | date: 2018-11-07 24 | url: https://lectures.fmi.golang.bg/05-concurrency101.slide 25 | 6: 26 | title: 06. Concurrency 102 27 | date: 2018-11-14 28 | url: https://lectures.fmi.golang.bg/06-concurrency102.slide 29 | 7: 30 | title: 07. Грешки, тестове и документация 31 | date: 2018-11-21 32 | url: https://lectures.fmi.golang.bg/07-errors_and_testing.slide 33 | 8: 34 | title: 08. За домашното 35 | date: 2018-11-28 36 | url: https://lectures.fmi.golang.bg/08-homework-retrospective.slide 37 | 9: 38 | title: 09. Go Tools 39 | date: 2018-12-05 40 | url: https://lectures.fmi.golang.bg/09-tools.slide 41 | 10: 42 | title: 10. Cgo и build дълбини 43 | date: 2018-12-12 44 | url: https://lectures.fmi.golang.bg/10-cgo-build.slide 45 | 11: 46 | title: 11. Управление на зависимости 47 | date: 2018-12-19 48 | url: https://lectures.fmi.golang.bg/11-dependency-management.slide 49 | 12: 50 | title: 12. Q&A с привкус на pprof 51 | date: 2019-01-09 52 | url: https://lectures.fmi.golang.bg/12-faq-and-pprof.slide 53 | 54 | --------------------------------------------------------------------------------