Как передать неограниченное количество аргументов разных типов
Anonymous Quiz
11%
func myFunc(args ...int)
11%
func myFunc(args []interface{})
75%
func myFunc(args ...interface{})
3%
Узнать ответ
Вам нужно выгрузить несколько строк из базы данных, какой метод будете исполльзовать?
Anonymous Quiz
51%
Query()
30%
QueryRow()
10%
Exec()
1%
Prepare()
0%
ProgLib()
6%
Узнать ответ
Сколько элементов хранится в одном bucket в map
Anonymous Quiz
10%
4
65%
8
4%
12
9%
16
12%
Узнать ответ
👣 Что выведет код ?

Ответ — 0 (nil-мапа позволяет использовать get-метод. А set-метод не даст скомпилировать код)
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Что выведет код ?

Ответ - [97 98 99]
Please open Telegram to view this post
VIEW IN TELEGRAM
Что точно выведет программа на экран при запуске?

Учитывай, что используется default в select, и что между отправками и чтениями есть time.Sleep.


package main

import (
"fmt"
"time"
)

func main() {
ch := make(chan int)
done := make(chan struct{})

go func() {
defer close(done)
for i := 0; i < 3; i++ {
ch <- i
time.Sleep(100 * time.Millisecond)
}
close(ch)
}()

go func() {
for {
select {
case v, ok := <-ch:
if !ok {
fmt.Println("channel closed")
return
}
fmt.Println("received:", v)
default:
fmt.Println("default case")
time.Sleep(50 * time.Millisecond)
}
}
}()

<-done
time.Sleep(500 * time.Millisecond)
}


Хинт: Код показывает, как работает select с default при чтении из канала — если данных нет, выбирается default, не блокируя выполнение.

Ответ:

🔄 Что делает программа:
Одна горутина пишет 0, 1, 2 в канал ch с паузой 100мс, затем закрывает канал.

Вторая горутина читает из ch через select:

если данные есть → received: N

если нет → default case

если канал закрыт → channel closed

📤 Что выведет (примерно):

default case
default case
received: 0
default case
received: 1
default case
received: 2
channel closed
Порядок может немного отличаться из-за гонки между горутинами, но общая структура будет именно такая.


@golangtests
Вопрос:
Что будет выведено на экран? Напиши полный вывод программы и объясни, почему именно так.


package main

import (
"fmt"
"time"
)

type Speaker interface {
Speak() string
}

type Person struct {
name string
}

func (p Person) Speak() string {
return "Hi, I'm " + p.name
}

func main() {
var s Speaker
p := Person{name: "Alice"}
s = p

p.name = "Bob"

fmt.Println(s.Speak()) // (1)

func() {
s := p // shadowing: s — это теперь Person, а не Speaker
fmt.Println(s.Speak()) // (2)
}()
go func(p Person) {
time.Sleep(10 * time.Millisecond)
fmt.Println(p.Speak()) // (3)
}(p)

p.name = "Charlie"

time.Sleep(20 * time.Millisecond)
}


💡Подсказки:
Что происходит с интерфейсами в Go при присвоении структур?

Что такое shadowing и как это влияет на s внутри анонимной функции?

Как работает передача аргументов в goroutine?

Как изменения структуры после передачи влияют на уже переданные значения?



Ответ:

Hi, I'm Alice
Hi, I'm Bob
Hi, I'm Bob


@golangtests
👣 Что выведет следующий код?

go 
package main
import (
"fmt"
)

func main() {
funcs := []func(){}

for i := 0; i < 3; i++ {
funcs = append(funcs, func() {
fmt.Println(i)
})
}

for _, f := range funcs {
f()
}
}


Варианты:

A)0 1 2
B)3 3 3
C) Паника на runtime
D) 0 0 0

💡 Пиши свой ответ в комментариях, и объясни почему!

Правильный ответ: 😎

🔍 Почему так происходит:
В этом коде создаётся срез из замыканий, и каждый func() внутри цикла ссылается на одну и ту же переменную i.

Ключевой момент: i не копируется при каждой итерации цикла, а продолжает изменяться, и все функции “запоминают” ссылку на одну и ту же i, а не её значение на момент создания.

К моменту вызова всех функций (f() в конце) цикл уже завершён, и значение i стало 3.

👉 Поэтому каждая функция выводит 3, а не 0, 1, 2.



@golangtests
Please open Telegram to view this post
VIEW IN TELEGRAM
🧪 Задача на Go 1.24: Range и поведение переменной цикла

Что выведет следующий код в Go 1.24?


package main

import "fmt"

func main() {
values := []int{10, 20, 30}
funcs := []func(){}

for v := range values {
funcs = append(funcs, func() {
fmt.Println(v)
})
}

for _, f := range funcs {
f()
}
}


Варианты ответа:
A)

1
2


B)

2
2


C)

20
30


D)

0
0


---

Правильный ответ: A

Почему:
В Go 1.24 при использовании
for v := range, переменная v копируется на каждой итерации, а не переиспользуется. Таким образом, замыкания получают своё собственное значение v на каждой итерации.
Здесь
range возвращает индексы (0, 1, 2), а не значения среза.

Чтобы получить значения (
10, 20, 30), нужно использовать for _, v := range values.
🧠 Что выведет следующий код?


package main

import (
"fmt"
)

func change(val *int) {
defer fmt.Println("defer:", *val)
*val = 20
fmt.Println("changed:", *val)
}

func main() {
num := 10
change(&num)
fmt.Println("main:", num)
}


Варианты ответа:
A)

defer: 20
main: 20


B)

defer: 10
main: 20


C)

changed: 20
main: 20


D)

defer: 20
main: 20


Код выведет:
changed: 20
defer: 10
main: 20


Объяснение:
main: Переменная num инициализируется значением 10.
change(&num): Вызывается функция change, и ей передается адрес переменной num (указатель на num). Теперь параметр val внутри change указывает на ту же ячейку памяти, где хранится num.
defer fmt.Println("defer:", *val): Внутри change встречается оператор defer.
Важно: Аргументы для отложенного вызова (*val в данном случае) вычисляются сразу в момент объявления defer, а не в момент выполнения отложенной функции. На этом этапе *val (значение по адресу, на который указывает val, то есть значение num) равно 10. Таким образом, вызов fmt.Println("defer:", 10) ставится в очередь на выполнение перед выходом из функции change.
*val = 20: Значение по адресу, на который указывает val, изменяется на 20. Поскольку val указывает на num, это напрямую изменяет значение переменной num в функции main на 20.
fmt.Println("changed:", *val): Выводится текущее значение по адресу val, которое теперь равно 20. Печатается changed: 20.
Конец change: Функция change собирается завершиться. Перед выходом выполняется отложенный вызов.
Выполнение defer: Выполняется ранее запланированный вызов fmt.Println("defer:", 10). Печатается defer: 10.
Возврат в main: Управление возвращается в main.
fmt.Println("main:", num): Выводится текущее значение переменной num. Так как функция change изменила его через указатель, num теперь равно 20. Печатается main: 20.

@golangtests
👣 Задача с подвохом, который связан с изменением поведения циклов, введенным в Go 1.22 (и, соответственно, актуальным для Go 1.24).

Задача: Что выведет следующий код при компиляции и запуске с использованием Go 1.24?


package main

import (
"fmt"
"sync"
"time"
)

func main() {
var wg sync.WaitGroup
count := 3

fmt.Println("Запуск горутин...")
wg.Add(count)

for i := 0; i < count; i++ {
// Запускаем горутину в каждой итерации
go func() {
defer wg.Done()
// Имитируем небольшую работу
time.Sleep(time.Duration(10) * time.Millisecond)
// Используем переменную цикла 'i' внутри горутины
fmt.Printf("Горутина видит i = %d\n", i)
}()
}

fmt.Println("Ожидание завершения горутин...")
wg.Wait()
fmt.Println("Все горутины завершены.")
}



Разбор:
Классическая проблема (до Go 1.22): В версиях Go до 1.22 существовала известная проблема с захватом переменных цикла в замыканиях (функциях, создаваемых внутри цикла, как наша анонимная горутина). Переменная i была одна на весь цикл.

Когда горутины реально начинали выполняться (после небольшой задержки time.Sleep), цикл for, скорее всего, уже завершался. К этому моменту переменная i имела бы свое конечное значение (в данном случае 3, так как цикл завершается, когда i становится равным count).

Поэтому в старых версиях Go этот код, скорее всего, вывел бы:
Запуск горутин...
Ожидание завершения горутин...
Горутина видит i = 3
Горутина видит i = 3
Горутина видит i = 3

Все горутины завершены.

(Порядок строк "Горутина видит..." мог быть разным). Чтобы обойти это, раньше приходилось делать так: i := i внутри цикла перед запуском горутины, чтобы создать копию переменной для каждой итерации.

Новое поведение (Go 1.22 и новее, включая 1.24): Начиная с Go 1.22, семантика переменных цикла for была изменена для предотвращения этой распространенной ошибки. Теперь переменная цикла (i в нашем случае) пересоздается для каждой итерации.

Это означает, что каждая горутина захватывает свою собственную копию i, соответствующую значению на момент этой итерации.

Ожидаемый вывод (Go 1.24): Благодаря изменению в Go 1.22, каждая горутина теперь корректно видит значение i той итерации, в которой она была запущена. Поэтому вывод будет (порядок строк "Горутина видит..." может варьироваться из-за недетерминированного планирования горутин):
Запуск горутин...
Ожидание завершения горутин...
Горутина видит i = 0
Горутина видит i = 1
Горутина видит i = 2
Все горутины завершены.
Use code with caution.
Или, например:
Запуск горутин...
Ожидание завершения горутин...
Горутина видит i = 2
Горутина видит i = 0
Горутина видит i = 1
Все горутины завершены.


Подвох заключается в том, что код выглядит как классический пример ошибки захвата переменной цикла, но из-за изменений в языке начиная с Go 1.22, он теперь работает "правильно" без необходимости явного копирования переменной (i := i).
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Что выйдет код ?

go 
package main

import "fmt"

// Пара обобщённого типа
type Pair[T any, U any] struct {
First T
Second U
}

// Метод, меняющий местами элементы
func (p Pair[T, U]) Swap() Pair[U, T] {
return Pair[U, T]{First: p.Second, Second: p.First}
}

// Карта обобщённого типа
type Mapping[K comparable, V any] map[K]V

func main() {
// явно указываем T=int, U=string
p1 := Pair[int, string]{First: 1, Second: "one"}

// начиная с Go 1.21 можно вывести параметры типа из литерала!
// здесь компилятор сам понимает, что T=string, U=int
p2 := Pair{"two", 2}

// аналогично для обобщённого alias-типа map
m := Mapping{"beta": 2}

// откатываем пару p2
swapped := p2.Swap()

fmt.Printf("%T %v\n", p2, p2)
fmt.Printf("%T %v\n", swapped, swapped)
fmt.Printf("%T %v\n", m, m)
}




Ответ: Код на Go 1.24 выведет следующее:

`main.Pair string int {two 2}`
`main.Pair int string {2 two}`
`main.Mapping string int map["beta":2]`

**Объяснение:**

1. **`p1 := Pairint, string{First: 1, Second: "one"}`** и **`p2 := Pair{"two", 2}`**:
* Создаются две переменные типа `Pair`.
* Для `p1` типы `T` (int) и `U` (string) указываются явно.
* Для `p2`, начиная с Go 1.21, компилятор автоматически выводит типы `T` (string) и `U` (int) из значений литерала `{"two", 2}`.

2. **`m := Mapping{"beta": 2}`**:
* Создается переменная `m` типа `Mapping`. `Mapping` является псевдонимом для `mapstringint`. Компилятор выводит типы ключа (string) и значения (int) из литерала `{"beta": 2}`.

3. **`swapped := p2.Swap()`**:
* Вызывается метод `Swap()` для `p2`. Метод `Swap()` возвращает новую структуру `Pair`, в которой типы `U` и `T` поменяны местами, а также значения полей `First` и `Second`. Таким образом, `swapped` будет иметь тип `Pairint, string` и значение `{2, "two"}`.

4. **`fmt.Printf("%T %v\n", p2, p2)`**:
* `%T` выводит тип переменной `p2`, который был выведен как `main.Pair string int`.
* `%v` выводит значение переменной `p2`, которое равно `{two 2}`.

5. **`fmt.Printf("%T %v\n", swapped, swapped)`**:
* `%T` выводит тип переменной `swapped`, который является результатом метода `Swap()` и равен `main.Pair int string`.
* `%v` выводит значение переменной `swapped`, которое равно `{2 two}`.

6. **`fmt.Printf("%T %v\n", m, m)`**:
* `%T` выводит тип переменной `m`, который является `main.Mapping string int`, что эквивалентно `mapstringint`.
* `%v` выводит значение переменной `m`, которое является картой `map["beta":2]`.

Таким образом, программа выведет тип и значение каждой из созданных и модифицированных переменных.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔍 Вопрос: что выведет этот код?

package main

import (
"fmt"
)

func main() {
m := map[string]int{"a": 1, "b": 2}
keys := make([]string, 0, len(m))

for k := range m {
keys = append(keys, k)
}

for i := range keys {
delete(m, keys[i])
m["z"] = i
}

fmt.Println("map:", m)
fmt.Println("keys:", keys)
}

---
Ответ: Вывод программы будет зависеть от порядка итерации по ключам в исходной мапе m.

В Go порядок итерации по элементам map не гарантируется и может меняться от запуска к запуску.
Рассмотрим возможные варианты развития событий, предполагая два наиболее вероятных порядка итерации:

Сценарий 1: Порядок итерации - "a", "b"
Итерация 1:
k становится "a".
keys становится ["a"].
Удаляется элемент с ключом "a" из m. m теперь {"b": 2}.
Добавляется элемент {"z": 0} в m. m теперь {"b": 2, "z": 0}.
Итерация 2:
k становится "b".
keys становится ["a", "b"].
Удаляется элемент с ключом "b" из m. m теперь {"z": 0}.
Добавляется элемент {"z": 1} в m (перезаписывая предыдущее значение). m теперь {"z": 1}.
В этом случае вывод будет:
map: map["z":1]
keys: [a b]
Сценарий 2: Порядок итерации - "b", "a"

Итерация 1:
k становится "b".
keys становится ["b"].
Удаляется элемент с ключом "b" из m. m теперь {"a": 1}.
Добавляется элемент {"z": 0} в m. m теперь {"a": 1, "z": 0}.

Итерация 2:
k становится "a".
keys становится ["b", "a"].
Удаляется элемент с ключом "a" из m. m теперь {"z": 0}.
Добавляется элемент {"z": 1} в m (перезаписывая предыдущее значение). m теперь {"z": 1}.
В этом случае вывод будет:
map: map["z":1]
keys: [b a]

Итог:
Гарантируется, что:
Значение в мапе m после выполнения цикла всегда будет map["z":1]. Это происходит потому, что в каждой итерации старое значение ключа "z" перезаписывается.

Слайс keys будет содержать ключи исходной мапы в том порядке, в котором они были получены при первой итерации. Это может быть ["a", "b"] или ["b", "a"], или какой-либо другой порядок, если мапа была больше.

Поэтому возможные варианты вывода:
Вариант 1:
map: map["z":1]
keys: [a b]
Вариант 2:
map: map["z":1]
keys: [b a]
Какой именно вариант вы увидите при запуске, предсказать невозможно из-за недетерминированного порядка итерации по map в Go.
🧠 Задача на Go (1.22+)


package main

import "fmt"

func main() {
m = map[string]int{"a": 1, "b": 2, "c": 3}

defer fmt.Println("done")

for k, v := range m {
defer fmt.Println(k, v)
}
}


Что выведет этот код?

A.
c 3
b 2
a 1
done

B.
done
a 1
b 2
c 3

C.
done
c 3
b 2
a 1

D.
a 3
a 3
a 3
done

Почему A? — в Go 1.22 range-переменные k и v создаются на каждом шаге заново, а defer захватывает копии, не ссылки.
Что выведет код?
🐹 Задача с подвохом: Generics и интерфейсы (Go 1.24)

Условие:

Что выведет следующий код и почему?


package main

import (
"fmt"
)

type Stringer interface {
String() string
}

type MyInt int

func (m MyInt) String() string {
return fmt.Sprintf("MyInt: %d", m)
}

func printValue[T any](val T) {
var s Stringer
if v, ok := any(val).(Stringer); ok {
s = v
}
if s != nil {
fmt.Println("Stringer:", s.String())
} else {
fmt.Println("Default:", val)
}
}

func main() {
var x MyInt = 5
printValue(x)
printValue(10)
}


Вопрос:
Что будет напечатано? Почему результат может удивить даже опытных Go-разработчиков?

🔍 Разбор:

Этот код использует generics и проверяет, реализует ли
val интерфейс Stringer.

Пошагово:

1️⃣
var x MyInt = 5
Тип
MyInt реализует интерфейс Stringer через метод:

```go
func (m MyInt) String() string { ... }
```

2️⃣
printValue(x)
-
val здесь типа MyInt.
- В строке:
any(val).(Stringer)
- Приведение типа успешно, потому что
MyInt реализует Stringer.
- Печатается:
```
Stringer: MyInt: 5
```

3️⃣
printValue(10)
- Тип
val здесь int.
-
int не реализует Stringer.
- Приведение
any(val).(Stringer) не срабатывает (ok == false).
- Печатается:
```
Default: 10
```

Ожидаемый вывод:

```
Stringer: MyInt: 5
Default: 10
```

💥 Подвох:

- Даже если
MyInt — это просто int, метод String() делает его другим типом, который реализует интерфейс.
- Многие забывают, что встроенные типы (int, string, и т.д.) никогда не реализуют интерфейсы напрямую, даже если они похожи на пользовательские типы.
- Ключевой момент:
any(val) оборачивает значение в интерфейс{}, поэтому приведение типа срабатывает только если исходный тип действительно реализует интерфейс.

🆕 Почему это актуально для Go 1.24:

Generics в Go 1.22+ улучшились по типовой проверке и стали более гибкими. Go 1.24 сделал generics более стабильными и оптимизированными, но все еще важно понимать различия между типами и интерфейсами, особенно при работе с
any и приведения типов внутри параметризованных функций.

🛡️ Лайфхак:

Если вы хотите печатать всё одинаково для типов с
String(), можно сделать generic-констрейн с интерфейсом:

```go
func printValue2[T fmt.Stringer](val T) {
fmt.Println("Stringer (generic):", val.String())
}
```

Вывод:

Generics упрощают код, но внимательность к типам и их интерфейсам критически важна. Даже небольшие различия между
int и MyInt влияют на поведение программы.
🤖 Хотите создать Telegram-бота с нуля на Go?

📅 13 мая в 20:00 МСК на открытом вебинаре мы разберем, как создать простого, но эффективного Telegram-бота для управления задачами, а также заложим правильную архитектуру для его дальнейшего расширения.

❗️Что разберем:

— Принципы разработки Telegram-ботов на Go.

— Архитектуру и структуры данных.

— Реализацию минимального набора функций для бота.

➡️ Открытый урок проходит в преддверие старта курса «Go (Golang) Developer Basic». Все участники получат скидку на обучение. Запишитесь прямо сейчас: https://otus.pw/4222/?erid=2W5zFGaggqW

Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.
🧠 Хитрая задача на Go: "Петля Блуждающего Робота"

Представь: у тебя есть робот, который бродит по бесконечной двумерной решетке. Он выполняет команды из строки:

- 'G' — идти вперед
- 'L' — повернуть налево (90°)
- 'R' — повернуть направо (90°)

Робот стартует из точки (0, 0) и смотрит на север. Команды повторяются бесконечно. Нужно определить:

👉 Зациклится ли движение робота?
(т.е. вернётся ли он в исходную точку или навсегда останется в замкнутом цикле)

📌 Примеры:


isLooping("GLGLGLG") => true // движется по квадрату
isLooping("GG") => false // уходит навсегда


🧩 Подвох задачи:

На первый взгляд кажется, что нужно бесконечно симулировать команды. Но на самом деле всё решается за один проход строки!
Важно: если после одного прохода робот:
- вернулся в (0,0) или
- сменил направление — значит, будет цикл.

Go-реализация:

```go
func isLooping(commands string) bool {
dirs := [][2]int{{0,1}, {1,0}, {0,-1}, {-1,0}} // север, восток, юг, запад
x, y, d := 0, 0, 0

for _, c := range commands {
switch c {
case 'G':
x += dirs[d][0]
y += dirs[d][1]
case 'L':
d = (d + 3) % 4
case 'R':
d = (d + 1) % 4
}
}

return (x == 0 && y == 0) || d != 0
}
```

🎯 Отличная задача для собеседования: она проверяет
• понимание направления и симуляции
• знание работы с векторами
• умение заменить бесконечный цикл на математический анализ


@golangtests
2025/05/13 00:19:03
Back to Top
HTML Embed Code: