Статья о человеке, которому Go помогает справляться с диабетом.
Если вкратце, то парень использует Go и распределённую систему микросервисов, чтобы отслеживать уровень инсулина и сахара в крови и т.д.
Параллельно в статье рассказывается про такие инструменты как Prometheus, Grafana, PagerDuty, так что можно узнать много полезного.
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
В Go можно писать в файлы без использования буферов, что означает, что каждая операция записи в файл приводит к записи на диск. Это поведение отличается от многих других языков программирования, где операции ввода-вывода буферизуются по дефолту для улучшения производительности.
Если вы хотите использовать буферизованный ввод-вывод, вы должны явно использовать пакет
bufio
для создания буферизованных объектов чтения и записи.В случае записи без использования буфера, каждый вызов
WriteString
приводит к непосредственной записи данных в файл, а с использованием буфера данные сначала записываются в него, и только после происходит запись данных в файл. Это может значительно улучшить производительность, особенно при большом количестве операций записи, поскольку уменьшает количество системных вызовов.В общем, использование буферизованного ввода-вывода может быть более эффективным по сравнению с небуферизованным.
При работе с большими файлами и необходимости минимизировать использование памяти, может быть целесообразно рассмотреть использование
bufio.Scanner
, который также использует буферизацию, но с более низким потреблением памяти.@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Loop:
for {
select {
case val, ok :=<- someChan:
if !ok {
// канал закрыт
break Loop
}
// получены данные, работа с val
default:
// канал пуст, что-то делать
}
}
Здесь,
ok
будет истиной только для открытого канала. Но при закрытии по каналу будут приходить пустые сообщения постоянно и ok
будет ложью.Однако вышеприведённая конструкция оптимальна только если нужно выполнять действие в
default
-ветке. Т.е. что-то делать, когда сообщений нет. Есть более простая и понятная конструкцияfor val := range someChan {
// получено сообщение
}
// канал закрыт
В этом случае канал читается, используя
for-range
. Когда канал будет закрыт и из него будет прочитано последнее сообщение, произойдёт выход из цикла.Запись в закрытый канал вызывает панику.
Дизайн приложения должен быть таким, что сторона создающая канал, его же и закрывает и пишет в него то же она. Более того, в этих случая рекомендуется, что б функция возвращала канал только для чтения
func run () <-chan string {
c := make(chan string)
// [...]
}
Конечно можно в отложенном вызове вызвать
recover()
, чтобы подавить панику. Но это в корне неправильно. @golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Среди заданных вопросов много интересных, типо:
— можно ли с Go и HTMX делать fullstack-приложения
— в чём отличие между namespace в других ЯП и package в Go
— как обстоят дела с Dependency Injection в Go
— и ещё несколько других
Отличное видео, помогает узнать об актуальных задачах, которые решаются с помощью Go и не только
Рекомендую)
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
rune
и byte
— в чём разница?rune
- это 32-х битный тип, представляющий юникодные символы в кодировке UTF-32 aka UCS-4.byte
- это универсальный 8-битный тип.rune
используется для работы с не-ASCII символами в строках. Есть встроенное приведение []rune
для типа string
, которое парсит строку из UTF-8
(представление с переменной длиной байтов) в представление с фиксированной длиной байтов.[]byte(str)
и []rune(str)
разительна. Строка Привет, мир!
:Байты: [208 159 209 128 208 184 208 178 208 181 209 130 44 32 208 188 208 184 209 128 33]
Руны: [1055 1088 1080 1074 1077 1090 44 32 1084 1080 1088 33]
[]rune(string)
эквивалентно вот такой функции:func ToRunes(bytes []byte) []rune {
result := []rune{}
for i := 0; i < len(bytes); {
r, size := utf8.DecodeRune(bytes[i:])
result = append(result, r)
i += size
}
return result
}
Привет, мир!
в руны и обратно@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
func HandleRequest(client http.Client, request *http.Request)
(*http.Response, error) {
response, err := client.Do(request)
defer response.Body.Close()
if err != nil {
return nil, err
}
}
А теперь вопрос: почему это должно работать, ведь тело всё равно закрыто, не зависимо до проверки или после?
Известно, что полное отсутствие строки
defer response.Body.Close()
приводит к гарантированным проблемам, но как протестировать вышеупомянутую уязвимость?defer response.Body.Close()
означает, что response.Body.Close()
должно быть выполнено в точке выхода из функции. Ни о каком закрытии тела до проверки ошибки речь не идёт.Проблема здесь в другом — в случае ошибки
response
может быть nil
, и выполнение отложенной инструкции в ветке if err != nil {... return ...}
в таком случае приведет к панике из-за обращения к нулевому указателю.panic: runtime error: invalid memory address or nil pointer dereference
А вот собственно и код:
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"os"
)
func main() {
client := http.Client{}
url, _ := url.Parse("https://no.such.host")
request := &http.Request{
Method: "GET",
URL: url,
}
response, err := client.Do(request)
defer response.Body.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "Request failed: %s\n", err.Error())
}
io.Copy(os.Stdout, response.Body)
}
Как-то так
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Здесь описывается абсолютно всё, что поможет подготовиться к собеседованию, и даже больше
Вопросы с собеседований связанные с Golang
├── Общие вопросы
├── Хеш-мапы
├── Интерфейсы
├── Пакеты
├── Типы данных
├── Defer
├── Примитивы синхронизации
├── Планировщик
├── Строки
├── Массивы и слайсы
├── Дженерики
├── Горутины
├── Конструкции
├── Гонка данных
├── Структуры
├── Контекст
├── Ошибки / Panic
└── Указатели
Вопросы с собеседований связанные с Linux
├── Файловая система
└── Сигналы, процессы
Вопросы по инфраструктуре
└── Базы данных (реляционные)
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Есть функция
strings.Contains
, но она проверяет есть ли строка в строке, не массив. Есть ли функция для этого, или надо писать свою?slices.IndexFunc
// искать индекс элемента target
idx := slices.IndexFunc(someSlice, func (elt E) { return elt == target })
if idx >= 0 { /* найден */ }
Но это реально тривиальная функция:
func IndexFunc[E any](s []E, f func(E) bool) int {
for i := range s {
if f(s[i]) {
return i
}
}
return -1
}
Если же массив отсортирован, то есть функция поиска делением пополам
slices.BinarySearch
- ищет для типов, для которых определено сравнение <
. В общем случае есть поиск по отсортированному слайсу с компаратором slices.BinarySearchFunc
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Например, тип
byte
из модуля "строки" — это алиас uint8
. Алиас объявляется через ключевое слово type
:type NumCount int
func main() {
nc := NumCount(len([]int{1, 2, 3}))
fmt.Println(nc) // 3
}
type errorCode string
func main() {
ec := errorCode("internal")
fmt.Println(ec) // internal
fmt.Println(string(ec)) // internal
}
type counter int
// передается указатель, чтобы можно было изменить состояние счетчика "c"
func (c *counter) inc() {
*c++
}
func main() {
c := counter(0)
(&c).inc() // передается указатель на счетчик &c, так как функция "inc()" работает с указателями
(&c).inc()
fmt.Println(c) // 2
}
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
// A struct with four anonymous fields of type T1, *T2, P.T3 and *P.T4
struct {
T1 // field name is T1
*T2 // field name is T2
P.T3 // field name is T3
*P.T4 // field name is T4
x, y int // field names are x and y
}
StructType = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl = (IdentifierList Type | AnonymousField) [ Tag ] .
AnonymousField = [ "*" ] TypeName .
Tag = string_lit .
Эти строки имеют отношение к граматикам, которые использует компилятор? Для чего эта информаия прописана в документации?
_
. Как его можно использовать?// A struct with 6 fields.
struct {
x, y int
u float32
_ float32 // padding
A *[]int
F func()
}
Что ж, давайте разберёмся со всеми вопросами:
type commonWidget struct {
width, height int
}
func (w *commonWidget) setHeight(height int) { w.height = height }
func (w *commonWidget) setWidth(width int) { w.width = width }
type button struct {
commonWidget
text string
}
type input struct {
commonWidget
type_ string
}
Здесь тип
commonWidget
описывает общие для всех виджетов поля и методы. Путём включения, типы button
и input
получают как нужные поля, так и методы, с ними работающие.gc
и gccgo
. Благодаря тому, что есть спецификация, когда два этих компилятора чем-то различаются, можно понять, кто "не прав". В свою очередь, различия в имплементации компиляторов часто указывают на неполноту спецификации.@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Wikipedia
Composition over inheritance
software design pattern
url
строки из punicode в unicode.Т.е. задача:
xn--go -uedfq
——> Go топ
. И наоборотidna
, сперва ставим пакет локально:go get golang.org/x/net/idna
Не забываем настроить переменную среды
%GOPATH
. После этого мы сможем спокойно импортировать пакет в проект:package main
import (
"fmt"
"golang.org/x/net/idna"
)
var p *idna.Profile
func main() {
p = idna.New()
fmt.Println(p.ToUnicode("xn--go -uedfq"))
}
// Go топ
Вот такие дела
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Создание простого сервера с поддержкой REST API на языке Go можно выполнить с использованием стандартной библиотеки
net/http
. Поехали!User
и создадим веб-сервер:package main
import (
"encoding/json"
"log"
"net/http"
"strconv"
"sync"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
var (
users = make(map[int]User)
mu sync.Mutex
idSeq int
)
func createUser(w http.ResponseWriter, r *http.Request) {
mu.Lock()
defer mu.Unlock()
idSeq++
user := User{ID: idSeq}
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
users[user.ID] = user
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(user)
}
func getUser(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.URL.Path[len("/users/"):])
if err != nil {
http.Error(w, "Invalid user ID", http.StatusBadRequest)
return
}
mu.Lock()
defer mu.Unlock()
user, ok := users[id]
if !ok {
http.NotFound(w, r)
return
}
json.NewEncoder(w).Encode(user)
}
func main() {
http.HandleFunc("/users", createUser)
http.HandleFunc("/users/", getUser)
log.Println("Starting server on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
8080
. Он обрабатывает два запроса:—
POST/users
: Создает нового пользователя. Ожидает JSON с именем пользователя в теле запроса. —
GET/users/{id}
: Возвращает данные пользователя по его ID.curl
или postman
. Например, для создания пользователя:curl -X POST -H "Content-Type: application/json" -d '{"name":"John Doe"}' http://localhost:8080/users
curl http://localhost:8080/users/1
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Держите годный контент
О чём вообще речь в этом видео?
В целом, обсуждается масса полезных вещей, так что видео стоит глянуть
@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM