Официальный новостной канал криптобиржи OKX | www.okx.com на русском языке.
? Комьюнити: t.me/okx_russian
?? Поддержка: [email protected]
АДМИН: @DaniiOKX
Маркетинг: @CoffeeTrends
Last updated 2 weeks, 2 days ago
Здесь простым языком про TON, DFC и крипту.
Принимаем автоматически.
Ссылка для друзей: https://t.me/+-EOfWx2pRKhmNGE6
Связь: @deftalk_bot
Last updated 1 month ago
#1 канал о блокчейне, криптовалютах и децентрализованных финансах.
🔥 Реклама — @DCTeam
Last updated 1 day, 6 hours ago
Go 1.23
Тут вышел Go 1.23, ну а мы с вами успели разобрать основные изменения заранее:
— Итераторы
— Таймеры
— Уникальные значения
— Скопировать каталог
— Куки
Все вместе с интерактивными примерами:
https://antonz.org/go-1-23
Большая крыса Go
Прежде чем вы решите, что я сошел с ума — речь на самом деле о типе big.Rat
.
В отличие от float64
, он позволяет работать с обыкновенными дробями (a/b) без потери точности.
Например, из школьного курса математики мы знаем, что 1/10 + 2/10 = 3/10. Однако, float64 другого мнения:
x := 0.1
y := 0.2
fmt.Println(x + y)
// 0.30000000000000004
А вот big.Rat справляется с такими вычислениями без проблем:
x := big.NewRat(1, 10)
y := big.NewRat(2, 10)
z := new(big.Rat)
z.Add(x, y)
fmt.Println(z)
// 3/10
Если вдруг придется работать с обыкновенными дробями — имейте «крысу» в виду.
Статический HTTP-сервер
Вы, наверно, слышали про встроенный в Python статический сервер:
python \-m http.server 8080
На Go его можно реализовать в десять строчек кода (плюс импорты):
```
func main() {
port := "8000"
if len(os.Args) > 1 {
port = os.Args[1]
}
fs := http.FileServer(http.Dir("."))
http.Handle("/", fs)
log.Printf("Serving HTTP on port %s...\n", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
```
И запускать вот так:
go run http.go 8080
Git в примерах
Зашел я на Степик в новые курсы, а там «Основы Git». В связи этим вспомнил, что у меня тоже есть интерактивная книга / сборник рецептов по гиту, называется Git by example.
Удобный краткий формат с конкретными примерами, я сам туда постоянно подглядываю (особенно в раздел undo).
Загляните и вы, если не обзавелись еще черным поясом по гиту.
https://antonz.org/git-by-example
P.S. А вы же знаете, что по Go тоже такая есть?
Безопасные ворота
Вот три способа безопасно закрыть ворота, один другого краше.
➊ sync.Mutex
Дубовый, но надежный способ. Защищаем изменение логического поля closed
мьютексом:
```
type Gates struct {
// признак закрытия ворот
closed bool
// мьютекс для защиты closed
mu sync.Mutex
}
func (g *Gates) Close() {
g.mu.Lock()
defer g.mu.Unlock()
if g.closed {
// игнорируем повторное закрытие
return
}
// закрыть ворота
g.closed = true
// освободить ресурсы
}
```
➋ atomic.Bool
Compare-and-set на атомарном bool гарантирует, что только одна горутина сможет поменять значение с false на true:
```
type Gates struct {
// признак закрытия ворот
closed atomic.Bool
}
func (g *Gates) Close() {
if !g.closed.CompareAndSwap(false, true) {
// игнорируем повторное закрытие
return
}
// закрыли ворота,
// можно освободить ресурсы
}
```
➌ sync.Once
Once.Do
гарантирует однократное выполнение в конкурентной среде, поэтому не приходится даже явно хранить состояние:
```
type Gates struct {
// гарантирует однократное выполнение
once sync.Once
}
func (g *Gates) Close() {
g.once.Do(func() {
// освободить ресурсы
})
}
```
Правда, такие ворота уже не получится открыть обратно, в отличие от предыдущих вариантов.
Кто ваш любимчик? Поделитесь в комментариях.
Опасные ворота
Вот наши ворота:
```
type Gates struct {
// признак закрытия ворот
closed chan struct{}
}
func (g *Gates) Close() {
select {
case <-g.closed:
// игнорируем повторное закрытие
return
default:
// закрыть ворота
close(g.closed)
// освободить ресурсы
}
}
```
Метод Close
— небезопасный. Если две горутины одновременно вызовут Close
, обе могут провалиться в default-ветку селекта, обе попытаются закрыть канал, и второе закрытие приведет к панике.
Другими словами, здесь гонки на закрытии канала. Селект сам по себе не защищает от гонок. Sad but true.
Что с этим делать — традиционно в следующей заметке.
Баян [::]
Гошный баян (вообще он «полносрезное выражение» или full slice expression, но «баян» мне ближе) имеет такой синтаксис:
s[low : high : max]
Баян создает срез длиной high\-low
и емкостью max\-low
. Используется крайне редко.
Чтобы понять разницу между обычным срезом и баяном, рассмотрим пример.
Как вы знаете, под каждым срезом лежит массив с данными (сам срез данных не содержит). Обычно этот массив создается неявно, но мы для наглядности сделаем так:
```
arr := [5]int{1, 2, 3}
// [1 2 3 0 0]
s := arr[0:3]
// [1 2 3]
len(s) // 3
cap(s) // 5
```
Срез s
указывает на массив arr
. Его длина (length) равна 3, а емкость (capacity, размер массива под срезом) равна 5.
Добавление элемента в срез добавляет его в массив, поскольку емкость это позволяет:
```
s = append(s, 4)
fmt.Println(arr)
// [1 2 3 4 0]
fmt.Println(s)
// [1 2 3 4]
```
А вот что будет, если создать срез с помощью баяна:
```
arr := [5]int{1, 2, 3}
// [1 2 3 0 0]
s := arr[0:3:3]
// [1 2 3]
len(s) // 3
cap(s) // 3
```
Все как раньше, только емкость среза равна 3. Поэтому добавление элемента в срез приведет к созданию нового массива под срезом. Исходный массив arr
не изменится:
```
s = append(s, 4)
fmt.Println(arr)
// [1 2 3 0 0]
fmt.Println(s)
// [1 2 3 4]
```
Такие дела. За разновидностями баянов приглашаю в комментарии.
?
Обновил курс до Go 1.22
Добавил в базовый курс по Go новые блоки и темы:
— range по целым числам;
— встроенные функции min, max и clear;
— комбинация ошибок через errors.Join;
— причина отмены контекста и context.AfterFunc (+ забористая задачка);
— дженерики, пакеты slices и maps (про них уже писал выше);
— тип-обертка Null в database/sql.
Заодно добавил маленький урок 1.9 со всякой всячиной (пока там блоки, группы и йота).
P.S. А еще на этом уроке вам предстоит победить Сквернолапа. Ну или Когтевика. В крайнем случае — Огнежора ?
Пустой срез vs. nil-срез
Как вы знаете, объявленная без инициализации переменная в Go автоматически получает нулевое значение соответствующего типа:
var num int // 0
var str string // ""
var flag bool // false
Для среза нулевое значение — nil:
var snil []int
// []int(nil)
С другой стороны, бывает инициализированный, но пустой срез:
sempty := []int{}
// or
// sempty = make([]int, 0)
Это разные значения, которые не равны между собой:
reflect.DeepEqual(snil, sempty)
// false
И в то же время, пустой срез и nil-срез почти всегда взаимозаменямы:
```
len(snil) // 0
cap(snil) // 0
snil = append(snil, 1) // []int{1}
len(sempty) // 0
cap(sempty) // 0
sempty = append(sempty, 1) // []int{1}
reflect.DeepEqual(snil, sempty)
// true
```
Почти всегда ваш код не должен делать разницы между пустым и nil-срезом.
Есть и исключения, конечно. Приглашаю поделиться ими в комментариях.
Официальный новостной канал криптобиржи OKX | www.okx.com на русском языке.
? Комьюнити: t.me/okx_russian
?? Поддержка: [email protected]
АДМИН: @DaniiOKX
Маркетинг: @CoffeeTrends
Last updated 2 weeks, 2 days ago
Здесь простым языком про TON, DFC и крипту.
Принимаем автоматически.
Ссылка для друзей: https://t.me/+-EOfWx2pRKhmNGE6
Связь: @deftalk_bot
Last updated 1 month ago
#1 канал о блокчейне, криптовалютах и децентрализованных финансах.
🔥 Реклама — @DCTeam
Last updated 1 day, 6 hours ago