Самые полезные каналы для программистов в одной подборке!
Сохраняйте себе, чтобы не потерять 💾
🔥Для всех
Библиотека программиста — новости, статьи, досуг, фундаментальные темы
Книги для программистов
IT-мемы
Proglib Academy — тут мы рассказываем про обучение и курсы
#️⃣C#
Библиотека шарписта
Библиотека задач по C# — код, квизы и тесты
Библиотека собеса по C# — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Вакансии по C#, .NET, Unity Вакансии по PHP, Symfony, Laravel
☁️DevOps
Библиотека devops’а
Вакансии по DevOps & SRE
Библиотека задач по DevOps — код, квизы и тесты
Библиотека собеса по DevOps — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
🐘PHP
Библиотека пхпшника
Вакансии по PHP, Symfony, Laravel
Библиотека PHP для собеса — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Библиотека задач по PHP — код, квизы и тесты
🐍Python
Библиотека питониста
Вакансии по питону, Django, Flask
Библиотека Python для собеса — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Библиотека задач по Python — код, квизы и тесты
☕Java
Библиотека джависта — полезные статьи по Java, новости и обучающие материалы
Библиотека Java для собеса — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Библиотека задач по Java — код, квизы и тесты
Вакансии для java-разработчиков
👾Data Science
Библиотека Data Science — полезные статьи, новости и обучающие материалы
Библиотека Data Science для собеса — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Библиотека задач по Data Science — код, квизы и тесты
Вакансии по Data Science, анализу данных, аналитике, искусственному интеллекту
🦫Go
Библиотека Go разработчика — полезные статьи, новости и обучающие материалы по Go
Библиотека Go для собеса — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Библиотека задач по Go — код, квизы и тесты
Вакансии по Go
🧠C++
Библиотека C/C++ разработчика — полезные статьи, новости и обучающие материалы по C++
Библиотека C++ для собеса — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Библиотека задач по C++ — код, квизы и тесты
Вакансии по C++
💻Другие профильные каналы
Библиотека фронтендера
Библиотека мобильного разработчика
Библиотека хакера
Библиотека тестировщика
💼Каналы с вакансиями
Вакансии по фронтенду, джаваскрипт, React, Angular, Vue
Вакансии для мобильных разработчиков
Вакансии по QA тестированию
InfoSec Jobs — вакансии по информационной безопасности
📁Чтобы добавить папку с нашими каналами, нажмите 👉сюда👈
🤖Также у нас есть боты:
Бот с IT-вакансиями
Бот с мероприятиями в сфере IT
Мы в других соцсетях:
🔸VK
🔸YouTube
🔸Дзен
🔸Facebook *
🔸Instagram *
* Организация Meta запрещена на территории РФ
Сохраняйте себе, чтобы не потерять 💾
🔥Для всех
Библиотека программиста — новости, статьи, досуг, фундаментальные темы
Книги для программистов
IT-мемы
Proglib Academy — тут мы рассказываем про обучение и курсы
#️⃣C#
Библиотека шарписта
Библиотека задач по C# — код, квизы и тесты
Библиотека собеса по C# — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Вакансии по C#, .NET, Unity Вакансии по PHP, Symfony, Laravel
☁️DevOps
Библиотека devops’а
Вакансии по DevOps & SRE
Библиотека задач по DevOps — код, квизы и тесты
Библиотека собеса по DevOps — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
🐘PHP
Библиотека пхпшника
Вакансии по PHP, Symfony, Laravel
Библиотека PHP для собеса — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Библиотека задач по PHP — код, квизы и тесты
🐍Python
Библиотека питониста
Вакансии по питону, Django, Flask
Библиотека Python для собеса — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Библиотека задач по Python — код, квизы и тесты
☕Java
Библиотека джависта — полезные статьи по Java, новости и обучающие материалы
Библиотека Java для собеса — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Библиотека задач по Java — код, квизы и тесты
Вакансии для java-разработчиков
👾Data Science
Библиотека Data Science — полезные статьи, новости и обучающие материалы
Библиотека Data Science для собеса — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Библиотека задач по Data Science — код, квизы и тесты
Вакансии по Data Science, анализу данных, аналитике, искусственному интеллекту
🦫Go
Библиотека Go разработчика — полезные статьи, новости и обучающие материалы по Go
Библиотека Go для собеса — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Библиотека задач по Go — код, квизы и тесты
Вакансии по Go
🧠C++
Библиотека C/C++ разработчика — полезные статьи, новости и обучающие материалы по C++
Библиотека C++ для собеса — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Библиотека задач по C++ — код, квизы и тесты
Вакансии по C++
💻Другие профильные каналы
Библиотека фронтендера
Библиотека мобильного разработчика
Библиотека хакера
Библиотека тестировщика
💼Каналы с вакансиями
Вакансии по фронтенду, джаваскрипт, React, Angular, Vue
Вакансии для мобильных разработчиков
Вакансии по QA тестированию
InfoSec Jobs — вакансии по информационной безопасности
📁Чтобы добавить папку с нашими каналами, нажмите 👉сюда👈
🤖Также у нас есть боты:
Бот с IT-вакансиями
Бот с мероприятиями в сфере IT
Мы в других соцсетях:
🔸VK
🔸YouTube
🔸Дзен
🔸Facebook *
🔸Instagram *
* Организация Meta запрещена на территории РФ
👍2❤1
💬 Что такое псевдоним типа (type alias) в Go?
Псевдоним типа — это функциональность, позволяющая создавать альтернативное имя для существующего типа данных. Это особенно полезно при рефакторинге кода, когда необходимо переименовать тип или сделать его более удобным для использования, не меняя основного определения типа.
Псевдонимы типов вводятся с использованием ключевого слова
📌 Пример:
Псевдоним типа — это функциональность, позволяющая создавать альтернативное имя для существующего типа данных. Это особенно полезно при рефакторинге кода, когда необходимо переименовать тип или сделать его более удобным для использования, не меняя основного определения типа.
Псевдонимы типов вводятся с использованием ключевого слова
type
, за которым следует новое имя типа, знак равенства и существующий тип. Например, type Bytes = []byte
. В этом случае Bytes
является псевдонимом для типа []byte
. Псевдонимы типов полностью идентичны их оригинальным типам, включая методы, связанные с типом.📌 Пример:
package main
import "fmt"
type ByteSlice = []byte
func printBytes(slice ByteSlice) {
for _, b := range slice {
fmt.Printf("%x ", b)
}
fmt.Println()
}
func main() {
data := ByteSlice{0x1, 0x2, 0x3}
printBytes(data) // Выведет: 1 2 3
}
👍11❤5
💬 Что такое захват переменной в Go?
Захват переменной в Go происходит в случаях, когда функция или другая конструкция использует переменные, объявленные за пределами своей собственной области видимости (скоупа).
Во вложенном скоупе есть возможность обращаться к переменным, объявленным в скоупе выше (но не наоборот). Обращение к переменным из вышестоящего скоупа — и есть их захват. Например:
Каждая функция в
Чтобы избежать этой проблемы и захватить текущее значение переменной внутри цикла, можно использовать локальную переменную внутри цикла:
Захват переменной в Go происходит в случаях, когда функция или другая конструкция использует переменные, объявленные за пределами своей собственной области видимости (скоупа).
Во вложенном скоупе есть возможность обращаться к переменным, объявленным в скоупе выше (но не наоборот). Обращение к переменным из вышестоящего скоупа — и есть их захват. Например:
func main() {
var funcs []func()
for i := 0; i < 3; i++ {
funcs = append(funcs, func() {
fmt.Println(i)
})
}
for _, f := range funcs {
f()
}
}
Каждая функция в
funcs
захватывает переменную i
. Однако, поскольку они захватывают саму переменную, а не ее значение на момент создания функции, все функции при вызове будут выводить одно и то же значение — значение i
после завершения цикла, то есть 3
.Чтобы избежать этой проблемы и захватить текущее значение переменной внутри цикла, можно использовать локальную переменную внутри цикла:
func main() {
var funcs []func()
for i := 0; i < 3; i++ {
i := i // создаем локальную переменную
funcs = append(funcs, func() {
fmt.Println(i)
})
}
for _, f := range funcs {
f()
}
}
Результат:
0
1
2
👍12🔥3
🧑💻 Статьи для IT: как объяснять и распространять значимые идеи
Напоминаем, что у нас есть бесплатный курс для всех, кто хочет научиться интересно писать — о программировании и в целом.
Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций.
Для кого: для авторов, копирайтеров и просто программистов, которые хотят научиться интересно рассказывать о своих проектах.
👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.
Напоминаем, что у нас есть бесплатный курс для всех, кто хочет научиться интересно писать — о программировании и в целом.
Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций.
Для кого: для авторов, копирайтеров и просто программистов, которые хотят научиться интересно рассказывать о своих проектах.
👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.
👍2
💬 Для каких целей в Go предназначены теги сборки (build tags)?
Теги сборки в Go предназначены для условной компиляции определенных частей кода в зависимости от заданных условий, таких как целевая ОС, архитектура или кастомные флаги. Это позволяет создавать более модульные и настраиваемые приложения, а также обеспечивает более гибкую организацию кода.
📌 Ключевые принципы:
1. Код, специфичный для платформы: например, наше приложение должно выполнять разные функции в зависимости от того, работает ли оно на Windows, Linux или macOS. В таком случае мы можем использовать теги сборки для включения соответствующего кода только для конкретной платформы. Например, файл
2. Экспериментальные фичи: теги сборки также могут использоваться для включения экспериментальных или еще не готовых фич. Например, если у нас есть экспериментальная функция, мы можем пометить её файл с помощью
3. Кастомные конфигурации: теги сборки позволяют создавать различные конфигурации нашего приложения. Например, мы можем использовать теги для сборки lite-версии приложения с ограниченным функционалом.
Теги сборки в Go предназначены для условной компиляции определенных частей кода в зависимости от заданных условий, таких как целевая ОС, архитектура или кастомные флаги. Это позволяет создавать более модульные и настраиваемые приложения, а также обеспечивает более гибкую организацию кода.
📌 Ключевые принципы:
1. Код, специфичный для платформы: например, наше приложение должно выполнять разные функции в зависимости от того, работает ли оно на Windows, Linux или macOS. В таком случае мы можем использовать теги сборки для включения соответствующего кода только для конкретной платформы. Например, файл
config_windows.go
может содержать конфигурации, специфичные для Windows, и будет иметь в первой строке комментарий // +build windows
.2. Экспериментальные фичи: теги сборки также могут использоваться для включения экспериментальных или еще не готовых фич. Например, если у нас есть экспериментальная функция, мы можем пометить её файл с помощью
// +build experimental
и компилировать приложение с этим тегом только тогда, когда захотим её включить.3. Кастомные конфигурации: теги сборки позволяют создавать различные конфигурации нашего приложения. Например, мы можем использовать теги для сборки lite-версии приложения с ограниченным функционалом.
🔥7👍2
💬 Можно ли в Go использовать оператор switch без условия?
Да, в Go можно использовать оператор
Здесь
Да, в Go можно использовать оператор
switch
без явного условия. В таком случае, switch
оценивает каждый case
как логическое выражение. Это позволяет создавать более чистый и читаемый код, когда нужен простой способ написания длинных цепочек в духе if-then-else
. Например:x := 42
switch {
case x > 100:
fmt.Println("x is very big")
case x > 10:
fmt.Println("x is big")
default:
fmt.Println("x is small")
}
Здесь
switch
последовательно проверяет каждое условие и выполняет код в блоке case
, который соответствует первому истинному условию.👍17🥰3🥱1
💬 На вход подаются два неупорядоченных среза любой длины. Напишите функцию на Go, которая возвращает их пересечение.
Несколько способов, которыми можно решить эту задачу:
1. Использование мапы: наиболее эффективный способ, особенно для больших срезов. Алгоритм:
☑️ Итерируем по первому срезу и добавляем каждый элемент в мапу.
☑️ Итерируем по второму срезу, проверяя наличие элемента в мапе.
☑️ Если элемент найден, добавляем его в результат.
2. Поэлементное сравнение: менее эффективный метод, особенно для больших срезов, поскольку его сложность — O(n*m), где n и m — размеры срезов.
☑️ Двойной цикл для сравнения каждого элемента одного среза с каждым элементом другого среза.
☑️ Если найдено совпадение, добавляем элемент в результат.
3. Сортировка: метод эффективен, если срезы большие и их можно изменять.
☑️ Сначала сортируем оба среза.
☑️ Затем используем два указателя, чтобы итерировать оба среза и находить совпадающие элементы.
📌 Простой пример:
Несколько способов, которыми можно решить эту задачу:
1. Использование мапы: наиболее эффективный способ, особенно для больших срезов. Алгоритм:
☑️ Итерируем по первому срезу и добавляем каждый элемент в мапу.
☑️ Итерируем по второму срезу, проверяя наличие элемента в мапе.
☑️ Если элемент найден, добавляем его в результат.
2. Поэлементное сравнение: менее эффективный метод, особенно для больших срезов, поскольку его сложность — O(n*m), где n и m — размеры срезов.
☑️ Двойной цикл для сравнения каждого элемента одного среза с каждым элементом другого среза.
☑️ Если найдено совпадение, добавляем элемент в результат.
3. Сортировка: метод эффективен, если срезы большие и их можно изменять.
☑️ Сначала сортируем оба среза.
☑️ Затем используем два указателя, чтобы итерировать оба среза и находить совпадающие элементы.
📌 Простой пример:
package main
import (
"fmt"
)
func Intersection(slice1, slice2 []int) []int {
// Создание мапы для хранения элементов первого среза
elements := make(map[int]bool)
for _, item := range slice1 {
elements[item] = true
}
// Поиск пересечений
var intersection []int
for _, item := range slice2 {
if _, found := elements[item]; found {
intersection = append(intersection, item)
// Чтобы избежать повторений в результате
delete(elements, item)
}
}
return intersection
}
func main() {
slice1 := []int{1, 3, 5, 7, 9}
slice2 := []int{3, 4, 5, 6, 7}
fmt.Println(Intersection(slice1, slice2)) // [3 5 7]
}
👍23❤1
💬 Каким образом в Go представлена хеш-таблица?
Хеш-таблица в Go представлена ключевым с
При увеличении количества элементов в мапе, количество бакетов может удваиваться, чтобы поддерживать эффективность операций доступа. В случае коллизий используются дополнительные overflow бакеты. Эта структура позволяет оптимизировать производительность при различных операциях с данными.
Хеш-таблица в Go представлена ключевым с
лов
ом map
. Мапа использует массив buckets (бакетов) для хранения пар ключ-значение. Ключи хешируются для определения, в какой бакет они должны быть помещены.При увеличении количества элементов в мапе, количество бакетов может удваиваться, чтобы поддерживать эффективность операций доступа. В случае коллизий используются дополнительные overflow бакеты. Эта структура позволяет оптимизировать производительность при различных операциях с данными.
👍4😁3
💬 Существует ли в Go короткий синтаксис для объявления условного оператора if?
Да, существует. Он позволяет объединить объявление переменной и условие в одном выражении. Этот синтаксис может пригодиться для управления областью видимости переменных и уменьшения объема кода.
📌 Простой пример:
Функция
Да, существует. Он позволяет объединить объявление переменной и условие в одном выражении. Этот синтаксис может пригодиться для управления областью видимости переменных и уменьшения объема кода.
📌 Простой пример:
if v, err := someFunction(); err != nil {
// обработка ошибки
} else {
// использование переменной v
}
Функция
someFunction()
возвращает два значения: результат (v
) и ошибку (err
). В блоке if
сначала выполняется вызов функции, затем проверяется значение err
. Если err
не равно nil
, выполняется блок кода для обработки ошибки. В противном случае, если err
равно nil
, выполняется блок else
, где доступна переменная v
. Переменные, объявленные в этой конструкции, ограничены областью видимости блока if
и else
.👍8
💬 Даны n каналов типа chan int. Напишите функцию, которая объединит все данные из этих каналов в один и вернет его.
Решить эту задачу можно, запустив горутину для каждого входного канала, которая будет читать данные из этого канала и отправлять их в один общий канал.
📌 Вот как это можно реализовать:
Функция
Для каждого входного канала создается горутина, которая читает данные из этого канала и отправляет их в общий канал
Решить эту задачу можно, запустив горутину для каждого входного канала, которая будет читать данные из этого канала и отправлять их в один общий канал.
📌 Вот как это можно реализовать:
package main
import (
"fmt"
"sync"
)
// mergeChannels объединяет данные из нескольких каналов в один
func mergeChannels(channels ...chan int) chan int {
var wg sync.WaitGroup
merged := make(chan int)
output := func(c chan int) {
for n := range c {
merged <- n
}
wg.Done()
}
wg.Add(len(channels))
for _, c := range channels {
go output(c)
}
// Закрыть merged канал после завершения всех горутин
go func() {
wg.Wait()
close(merged)
}()
return merged
}
func main() {
// Пример использования
c1 := make(chan int)
c2 := make(chan int)
c3 := make(chan int)
// Заполнение каналов данными
go func() { c1 <- 1; close(c1) }()
go func() { c2 <- 2; close(c2) }()
go func() { c3 <- 3; close(c3) }()
// Объединение каналов
for n := range mergeChannels(c1, c2, c3) {
fmt.Println(n)
}
}
Функция
mergeChannels
принимает переменное количество каналов chan int
и возвращает один канал, в который будут направлены все данные из входных каналов. Для каждого входного канала создается горутина, которая читает данные из этого канала и отправляет их в общий канал
merged
. Использование sync.WaitGroup
позволяет дождаться завершения всех горутин, после чего общий канал закрывается.👍21❤4
💬 Чем пустой интерфейс отличается от nil интерфейса в Go?
1. Пустой интерфейс в Go не имеет определенных методов. Это означает, что он может содержать значение любого типа, так как каждый тип в Go, по умолчанию, удовлетворяет интерфейсу без методов.
Пустые интерфейсы широко используются в Go для работы со значениями неизвестного типа, поскольку они могут хранить любой тип данных.
2.
Важно отметить, что если интерфейс хранит указатель, который является
1. Пустой интерфейс в Go не имеет определенных методов. Это означает, что он может содержать значение любого типа, так как каждый тип в Go, по умолчанию, удовлетворяет интерфейсу без методов.
Пустые интерфейсы широко используются в Go для работы со значениями неизвестного типа, поскольку они могут хранить любой тип данных.
2.
Nil
интерфейс в Go — это интерфейс, у которого не установлены ни тип, ни значение. Такой интерфейс не ссылается ни на какой объект или значение. Важно отметить, что если интерфейс хранит указатель, который является
nil
, сам интерфейс при этом не будет nil
. Это значит, что интерфейс с nil
значением отличается от полностью nil
интерфейса.👏14👍8
💬Как можно гарантировать, что тип удовлетворяет интерфейсу?
Мы можем «попросить» компилятор проверить, реализует ли тип
Если
Этот метод не выполняет никакой реальной функции, но служит как явное подтверждение того, что тип предназначен для соответствия данному интерфейсу. Таким образом, любой тип, который должен соответствовать интерфейсу, обязан реализовать этот метод, ясно демонстрируя свою совместимость с интерфейсом. Например:
Тип должен тогда реализовать метод
Большинство кода не использует такие ограничения, поскольку они ограничивают полезность идеи интерфейса. Однако иногда они необходимы для разрешения неоднозначностей между похожими интерфейсами.
Мы можем «попросить» компилятор проверить, реализует ли тип
T
интерфейс I
, попытавшись выполнить присваивание с использованием нулевого значения для T
или указателя на T
, в зависимости от ситуации:type T struct{}
var _ I = T{} // Проверяем, реализует ли T интерфейс I
var _ I = (*T)(nil) // Проверяем, реализует ли *T интерфейс I
Если
T
(или *T
соответственно) не реализует I
, ошибка будет обнаружена во время компиляции. Если мы хотим, чтобы реализация интерфейса была более очевидной, мы можем включить в определение интерфейса специальный метод с уникальным названием. Этот метод не выполняет никакой реальной функции, но служит как явное подтверждение того, что тип предназначен для соответствия данному интерфейсу. Таким образом, любой тип, который должен соответствовать интерфейсу, обязан реализовать этот метод, ясно демонстрируя свою совместимость с интерфейсом. Например:
type Fooer interface {
Foo()
ImplementsFooer()
}
Тип должен тогда реализовать метод
ImplementsFooer
, чтобы быть Fooer
, ясно документируя этот факт и объявляя его в выводе go doc.type Bar struct{}
func (b Bar) ImplementsFooer() {}
func (b Bar) Foo() {}
Большинство кода не использует такие ограничения, поскольку они ограничивают полезность идеи интерфейса. Однако иногда они необходимы для разрешения неоднозначностей между похожими интерфейсами.
pkg.go.dev
go command - cmd/go - Go Packages
Go is a tool for managing Go source code.
🔥12💯1
💬 Будет ли программа на Go работать быстрее при увеличении количества CPU?
Ускорение работы программы с увеличением числа CPU зависит от решаемой задачи. Go предоставляет примитивы для работы с конкурентностью, такие как горутины и каналы, но конкурентность позволяет реализовать параллелизм только тогда, когда сама задача по своей природе параллельна. Задачи, которые по своей сути последовательны, не могут быть ускорены добавлением CPU, в то время как задачи, которые можно разбить на части, выполняемые параллельно, могут быть ускорены, иногда значительно.
Иногда добавление большего количества CPU может замедлить программу. На практике программы, которые тратят больше времени на синхронизацию или коммуникацию, чем на полезные вычисления, могут испытывать снижение производительности при использовании нескольких потоков операционной системы. Это связано с тем, что передача данных между потоками влечет за собой смену контекстов, что обходится «дорого», и эти затраты могут увеличиваться с ростом числа CPU. Например, пример с prime sieve из спецификации Go не имеет значительного параллелизма, хотя в нем запускается множество горутин; увеличение числа потоков (CPU) скорее замедлит его, чем ускорит.
Ускорение работы программы с увеличением числа CPU зависит от решаемой задачи. Go предоставляет примитивы для работы с конкурентностью, такие как горутины и каналы, но конкурентность позволяет реализовать параллелизм только тогда, когда сама задача по своей природе параллельна. Задачи, которые по своей сути последовательны, не могут быть ускорены добавлением CPU, в то время как задачи, которые можно разбить на части, выполняемые параллельно, могут быть ускорены, иногда значительно.
Иногда добавление большего количества CPU может замедлить программу. На практике программы, которые тратят больше времени на синхронизацию или коммуникацию, чем на полезные вычисления, могут испытывать снижение производительности при использовании нескольких потоков операционной системы. Это связано с тем, что передача данных между потоками влечет за собой смену контекстов, что обходится «дорого», и эти затраты могут увеличиваться с ростом числа CPU. Например, пример с prime sieve из спецификации Go не имеет значительного параллелизма, хотя в нем запускается множество горутин; увеличение числа потоков (CPU) скорее замедлит его, чем ускорит.
go.dev
The Go Programming Language Specification - The Go Programming Language
👍14
💬 Что происходит с замыканиями, выполняемыми как горутины?
При использовании замыканий с конкурентностью может возникнуть путаница. Рассмотрим пример:
Можно ошибочно ожидать увидеть следующий вывод:
Когда замыкание выполняется, оно выводит значение
Чтобы привязать текущее значение
В этом примере значение
Еще проще — создать новую переменную, используя стиль объявления, который может показаться странным, но в Go работает хорошо:
При использовании замыканий с конкурентностью может возникнуть путаница. Рассмотрим пример:
func main() {
done := make(chan bool)
values := []string{"a", "b", "c"}
for _, v := range values {
go func() {
fmt.Println(v)
done <- true
}()
}
// ждем завершения всех горутин перед выходом
for _ = range values {
<-done
}
}
Можно ошибочно ожидать увидеть следующий вывод:
a
, b
, c
. Вместо этого получаем c
, c
, c
. Это происходит потому, что каждая итерация цикла использует один и тот же экземпляр переменной v
, так что каждое замыкание делится этой единственной переменной. Когда замыкание выполняется, оно выводит значение
v
в момент выполнения fmt.Println
, но v
могла измениться с момента запуска горутины. Чтобы помочь обнаружить эту и другие проблемы до их возникновения, необходимо использовать go vet
.Чтобы привязать текущее значение
v
к каждому замыканию при его запуске, необходимо изменить внутренний цикл так, чтобы на каждой итерации создавалась новая переменная. Один из способов — передать переменную в качестве аргумента замыканию:for _, v := range values {
go func(u string) {
fmt.Println(u)
done <- true
}(v)
}
В этом примере значение
v
передается в качестве аргумента анонимной функции. Это значение затем доступно внутри функции как переменная u
.Еще проще — создать новую переменную, используя стиль объявления, который может показаться странным, но в Go работает хорошо:
for _, v := range values {
v := v // создаем новую 'v'.
go func() {
fmt.Println(v)
done <- true
}()
}
🔥12👍9
💬 Почему значение ошибки типа nil не равно nil?
Под капотом интерфейсы реализованы как два элемента: тип
Значение
Значение интерфейса является
Эта ситуация может быть запутанной и возникает, когда
Если всё идет хорошо, функция возвращает
Хорошей практикой для функций, возвращающих ошибки, является использование типа
Под капотом интерфейсы реализованы как два элемента: тип
T
и значение V
. V
— это конкретное значение, такое как int, структура или указатель. Например, если мы сохраняем значение int 3 в интерфейсе, полученное значение интерфейса схематически будет (T=int
, V=3
). Значение
V
также известно как динамическое значение интерфейса, поскольку одна и та же переменная интерфейса может хранить разные значения V
(и соответствующие типы T
) во время выполнения программы.Значение интерфейса является
nil
только если и V
, и T
не установлены. В частности, nil
интерфейс всегда будет содержать nil
тип. Если мы сохраняем nil
указатель типа *int
внутри значения интерфейса, внутренний тип будет *int
независимо от значения указателя: (T=*int
, V=nil
). Таким образом, значение интерфейса будет не nil
, даже когда значение указателя V
внутри является nil
.Эта ситуация может быть запутанной и возникает, когда
nil
значение сохраняется внутри значения интерфейса, например, в возвращаемом значении ошибки:func returnsError() error {
var p *MyError = nil
if bad() {
p = ErrBad
}
return p // Всегда вернет не nil ошибку.
}
Если всё идет хорошо, функция возвращает
nil
p
, так что возвращаемое значение — это значение интерфейса ошибки, содержащее (T=*MyError
, V=nil
). Это означает, что если вызывающий сравнивает возвращенную ошибку с nil
, он всегда будет видеть, как будто произошла ошибка, даже если ничего плохого не случилось. Чтобы вернуть правильную nil
ошибку вызывающему, функция должна явно вернуть nil
:func returnsError() error {
if bad() {
return ErrBad
}
return nil
}
Хорошей практикой для функций, возвращающих ошибки, является использование типа
error
в их сигнатуре (как сделано выше), а не конкретного типа, например *MyError
, чтобы помочь гарантировать, что ошибка создается правильно.👍20
💬 Почему Go-процесс может использовать много виртуальной памяти?
Аллокатор памяти в Go резервирует большую область виртуальной памяти как арену для выделения. Эта виртуальная память локальна для конкретного процесса Go; резервирование не лишает другие процессы памяти.
Чтобы узнать количество фактически выделенной памяти процессу Go, можно использовать команду
Аллокатор памяти в Go резервирует большую область виртуальной памяти как арену для выделения. Эта виртуальная память локальна для конкретного процесса Go; резервирование не лишает другие процессы памяти.
Чтобы узнать количество фактически выделенной памяти процессу Go, можно использовать команду
top
в Unix (столбцы RES
(Linux) или RSIZE
(macOS)).❤1
⚡️Самые полезные каналы по Go в одной папке
В ней:
➖ интересные задачи
➖ основной канал
➖ книги по Go
➖ лучшие вакансии из сферы
➖ и наш чат, в котором можно общаться и задавать вопросы
Добавляйте 👉 тык сюда
В ней:
Добавляйте 👉 тык сюда
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4❤2👍2
💬 Можно ли преобразовать []T в []interface{}?
Не напрямую. Спецификация Go запрещает это, поскольку эти два типа имеют разное представление в памяти. Необходимо копировать элементы по отдельности в целевой срез.
В примере срез типа
Не напрямую. Спецификация Go запрещает это, поскольку эти два типа имеют разное представление в памяти. Необходимо копировать элементы по отдельности в целевой срез.
В примере срез типа
int
преобразуется в срез типа interface{}
:t := []int{1, 2, 3, 4}
s := make([]interface{}, len(t))
for i, v := range t {
s[i] = v
}
👍10
🧑💻 Статьи для IT: как объяснять и распространять значимые идеи
Напоминаем, что у нас есть бесплатный курс для всех, кто хочет научиться интересно писать — о программировании и в целом.
Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций.
Для кого: для авторов, копирайтеров и просто программистов, которые хотят научиться интересно рассказывать о своих проектах.
👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.
Напоминаем, что у нас есть бесплатный курс для всех, кто хочет научиться интересно писать — о программировании и в целом.
Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций.
Для кого: для авторов, копирайтеров и просто программистов, которые хотят научиться интересно рассказывать о своих проектах.
👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.
❤1
💬 Как в Go осуществляется доступ к полям структуры?
В Go доступ к полям структуры осуществляется с использованием символа '
Поля структуры могут быть как экспортируемыми (с заглавной буквы), так и неэкспортируемыми (со строчной буквы). Экспортируемые доступны вне пакета, в котором определена структура, в то время как неэкспортируемые доступны только внутри пакета.
📌 Простой пример:
Доступ к полям структуры осуществляется через экземпляр
В Go доступ к полям структуры осуществляется с использованием символа '
.
'. Каждое поле структуры имеет своё имя, и к этим полям можно обращаться, используя имя экземпляра структуры, за которым следует точка и имя поля. Поля структуры могут быть как экспортируемыми (с заглавной буквы), так и неэкспортируемыми (со строчной буквы). Экспортируемые доступны вне пакета, в котором определена структура, в то время как неэкспортируемые доступны только внутри пакета.
📌 Простой пример:
package main
import "fmt"
// Определяем структуру Person
type Person struct {
Name string // Экспортируемое поле
age int // Неэкспортируемое поле
}
func main() {
// Создаем экземпляр структуры Person
p := Person{Name: "Alice", age: 30}
// Доступ к экспортируемому полю Name
fmt.Println("Name:", p.Name)
// Доступ к неэкспортируемому полю age (возможен, т. к. мы находимся в том же пакете)
fmt.Println("Age:", p.age)
}
Доступ к полям структуры осуществляется через экземпляр
p
структуры Person
с использованием точки (p.Name
и p.age
).🥱22👍8❤2