Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
SSL (Secure Sockets Layer) — это протокол, который обеспечивает безопасность передачи данных в интернете, используя шифрование. Он был разработан для защиты данных, передаваемых между клиентом (например, веб-браузером) и сервером (например, веб-сайтом), от перехвата и манипуляций.
Защита данных от перехвата и чтения посторонними лицами путем их шифрования.
Подтверждение подлинности сервера (и иногда клиента) с помощью цифровых сертификатов, что позволяет клиенту убедиться, что он подключен к настоящему серверу.
Проверка того, что данные не были изменены во время передачи, с помощью контрольных сумм и хеш-функций.
Клиент инициирует соединение с сервером, запрашивая защищенное соединение.
Сервер отправляет свой цифровой сертификат, который содержит его публичный ключ и информацию о сервере.
Клиент проверяет сертификат, используя доверенные центры сертификации (CA), чтобы удостовериться в подлинности сервера.
Клиент и сервер используют асимметричное шифрование для обмена ключами сеанса, которые затем используются для симметричного шифрования данных в течение сессии.
Все данные, передаваемые между клиентом и сервером, шифруются с использованием симметричных ключей, обеспечивая безопасность передачи.
При посещении веб-сайта с использованием HTTPS (например, https://example.com), SSL обеспечивает шифрование и безопасность данных, передаваемых между вашим браузером и сервером.
Никогда не был выпущен публично из-за серьезных уязвимостей.
Выпущен в 1995 году, но вскоре был признан небезопасным из-за множества уязвимостей.
Выпущен в 1996 году, значительно улучшил безопасность, но со временем также был признан устаревшим из-за уязвимостей (например, POODLE-атака).
SSL был заменен протоколом TLS, который является его преемником и предлагает улучшенную безопасность. Текущие версии TLS (1.2 и 1.3) используются вместо SSL.
TLS обеспечивает более сильное шифрование, лучшее управление сессионными ключами и устранение уязвимостей, найденных в SSL.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Git — это распределённая система контроля версий, которая позволяет:
- Отслеживать изменения в коде.
- Создавать ветки и экспериментировать без риска.
- Сотрудничать над проектами в команде. Git хранит историю в виде снимков состояний (commit), а не разницы (diff), что делает его быстрым и надёжным.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это целочисленные значения, которые используются для доступа к элементам упорядоченных структур данных. В контексте Go индексы чаще всего применяются для работы со строками, массивами, срезами, а также картами (косвенно, через ключи).
Индексы позволяют обращаться к конкретным элементам массива, строки или среза. Например, если у нас есть массив чисел, индекс указывает, какой именно элемент извлечь.
С помощью индексов можно перебирать элементы массива, строки или среза, например, используя циклы.
В изменяемых структурах данных, таких как срезы или массивы, индекс позволяет присвоить новое значение конкретному элементу.
Индексы упрощают и ускоряют доступ к данным, потому что доступ осуществляется за O(1) (константное время) в массивах или срезах.
В строках индексы используются для доступа к конкретным байтам.
package main
import "fmt"
func main() {
str := "Привет"
fmt.Println(str[0]) // 208 (байт, не символ!)
fmt.Printf("%c\n", str[0]) // П (символ, представленный первым байтом UTF-8)
}
В массивах и срезах индексы используются для извлечения и изменения значений
package main
import "fmt"
func main() {
arr := [5]int{10, 20, 30, 40, 50}
fmt.Println(arr[2]) // 30
// Изменение значения по индексу
arr[2] = 100
fmt.Println(arr) // [10 20 100 40 50]
}
Обычно индексы используются для итерации по элементам коллекции с помощью цикла
for
.package main
import "fmt"
func main() {
nums := []int{10, 20, 30, 40, 50}
for i, v := range nums {
fmt.Printf("Индекс: %d, Значение: %d\n", i, v)
}
}
Индексы полезны для извлечения подстрок с использованием срезов:
package main
import "fmt"
func main() {
str := "Привет, Мир!"
fmt.Println(str[8:12]) // Мир
}
Если попытаться обратиться к элементу по индексу, который выходит за пределы коллекции, Go выдаст runtime panic:
package main
func main() {
nums := []int{1, 2, 3}
fmt.Println(nums[5]) // panic: runtime error: index out of range
}
Если неверно учитывать байтовое представление символов UTF-8, можно получить некорректный результат.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Куча — это область памяти, где:
- Выделение динамическое (во время выполнения).
- Используется для объектов переменной длины и длительного хранения.
- Управляется аллокатором или сборщиком мусора.
- Может фрагментироваться.
- Поддерживает аллокации разного размера, отслеживает свободные блоки.
В языках с GC куча управляется автоматически. В C/C++ — вручную через malloc/free.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это неизменяемые последовательности байтов, обычно представляющие текст в кодировке UTF-8. Работа с ними в Go основывается на использовании встроенных функций, методов стандартной библиотеки и специальных типов данных.
Самый простой способ объединить строки — использовать оператор
+
или функцию fmt.Sprintf
.package main
import "fmt"
func main() {
s1 := "Привет"
s2 := "Мир"
result := s1 + ", " + s2 + "!"
fmt.Println(result) // Привет, Мир!
// Через fmt.Sprintf
formatted := fmt.Sprintf("%s, %s!", s1, s2)
fmt.Println(formatted) // Привет, Мир!
}
Функция
len
возвращает длину строки в байтах, а не в символах.package main
import "fmt"
func main() {
str := "Привет"
fmt.Println(len(str)) // 12, так как кириллические символы занимают 2 байта
}
Строки можно обойти как в виде байтов, так и в виде рун (Unicode символов).
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
str := "Привет"
// Итерация по байтам
for i := 0; i < len(str); i++ {
fmt.Printf("Байт: %x\n", str[i])
}
// Итерация по символам (рунам)
for _, runeValue := range str {
fmt.Printf("Символ: %c\n", runeValue)
}
// Количество рун
fmt.Println("Количество символов:", utf8.RuneCountInString(str))
}
Так как строки неизменяемы, для извлечения подстроки используется срез (
slice
).package main
import "fmt"
func main() {
str := "Привет, Мир!"
substring := str[8:] // С 8-го байта до конца
fmt.Println(substring) // Мир!
}
Функции
strings.Split
и strings.Join
из пакета strings
позволяют разбивать строку на части или объединять части в строку.package main
import (
"fmt"
"strings"
)
func main() {
str := "apple,banana,cherry"
// Разделение строки
parts := strings.Split(str, ",")
fmt.Println(parts) // [apple banana cherry]
// Объединение строк
joined := strings.Join(parts, " | ")
fmt.Println(joined) // apple | banana | cherry
}
Для поиска подстроки можно использовать
strings.Contains
, strings.Index
или strings.HasPrefix/strings.HasSuffix
. Для замены — strings.Replace
.package main
import (
"fmt"
"strings"
)
func main() {
str := "Go — это круто!"
// Поиск подстроки
fmt.Println(strings.Contains(str, "круто")) // true
fmt.Println(strings.Index(str, "это")) // 4
// Замена подстроки
newStr := strings.Replace(str, "круто", "отлично", 1)
fmt.Println(newStr) // Go — это отлично!
}
Стандартная библиотека предоставляет функции для изменения регистра строки:
strings.ToLower
и strings.ToUpper
.package main
import (
"fmt"
"strings"
)
func main() {
str := "Hello, Go!"
fmt.Println(strings.ToUpper(str)) // HELLO, GO!
fmt.Println(strings.ToLower(str)) // hello, go!
}
Удаление пробелов или других символов с начала/конца строки выполняется с помощью
strings.Trim
, strings.TrimSpace
и других функций.package main
import (
"fmt"
"strings"
)
func main() {
str := " Hello, Go! "
fmt.Println(strings.TrimSpace(str)) // "Hello, Go!"
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Нельзя: Изменять данные внутри канала без защиты, если они используются несколькими горутинами.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Структуры (struct) представляют собой агрегированные типы данных, которые позволяют объединять несколько различных типов данных под одним именем. Они служат для моделирования объектов и хранения связанных данных, предоставляя удобный способ управления сложными данными.
Определяются с использованием ключевого слова
struct
. В структуре могут быть поля различных типов.package main
import "fmt"
// Определение структуры Person
type Person struct {
Name string
Age int
}
func main() {
// Создание экземпляра структуры
var p Person
p.Name = "Alice"
p.Age = 30
fmt.Println("Name:", p.Name)
fmt.Println("Age:", p.Age)
}
Существует несколько способов инициализации структур.
package main
import "fmt"
// Определение структуры Person
type Person struct {
Name string
Age int
}
func main() {
// Инициализация с использованием литерала структуры
p := Person{Name: "Bob", Age: 25}
fmt.Println("Name:", p.Name)
fmt.Println("Age:", p.Age)
}
Инициализация по умолчанию
package main
import "fmt"
// Определение структуры Person
type Person struct {
Name string
Age int
}
func main() {
// Инициализация по умолчанию (поля будут нулевыми значениями)
var p Person
fmt.Println("Name:", p.Name) // Пустая строка
fmt.Println("Age:", p.Age) // 0
}
Могут содержать другие структуры в качестве полей, что позволяет моделировать более сложные данные.
package main
import "fmt"
// Определение структур Address и Person
type Address struct {
City string
State string
}
type Person struct {
Name string
Age int
Address Address
}
func main() {
// Инициализация структуры с вложенной структурой
p := Person{
Name: "Charlie",
Age: 40,
Address: Address{
City: "New York",
State: "NY",
},
}
fmt.Println("Name:", p.Name)
fmt.Println("Age:", p.Age)
fmt.Println("City:", p.Address.City)
fmt.Println("State:", p.Address.State)
}
Могут быть ассоциированы со структурами, что позволяет добавлять функциональность к структурам.
package main
import "fmt"
// Определение структуры Person
type Person struct {
Name string
Age int
}
// Метод для структуры Person
func (p Person) Greet() {
fmt.Printf("Hello, my name is %s and I am %d years old.\n", p.Name, p.Age)
}
func main() {
p := Person{Name: "David", Age: 35}
p.Greet()
}
Позволяют логически объединять связанные данные в один тип.
Позволяют моделировать реальные объекты и их свойства.
Использование структур делает код более организованным и понятным.
Могут иметь методы, что позволяет добавлять функциональность и поведение объектам.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Эти термины часто встречаются в gRPC и API:
- Unary — классический вызов: клиент делает запрос, сервер возвращает единственный ответ.
- Stream — потоковая передача:
- Server streaming — сервер отправляет несколько ответов.
- Client streaming — клиент отправляет поток данных.
- Bidirectional — оба обмениваются потоками данных.
Streaming нужен, когда нужно передавать большие объёмы или непрерывный поток данных.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В Go механизм сборки мусора называется Garbage Collector (GC).
Go использует автоматический сборщик мусора с конкурентной, трицветной, инкрементальной стратегией.
выполняется параллельно с работой программы.
объекты помечаются как белые, серые и чёрные.
работает порциями, а не останавливает программу надолго.
мусор, который будет удалён.
те, которые находятся в обработке.
используемые объекты, которые не подлежат удалению.
Чтобы уменьшить нагрузку на GC:
Используйте пул объектов (
sync.Pool
). Минимизируйте аллокации в куче (например, используйте
[]byte
вместо string
, если можно). Ограничивайте долгоживущие объекты, так как они реже сканируются.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Алгоритмы балансировки нагрузки:
- Round Robin — по кругу, равномерно.
- Least Connections — запрос уходит на сервер с наименьшим числом активных соединений.
- IP Hash — запросы одного клиента попадают на один сервер.
- Weighted Round Robin — с учётом мощности/приоритета серверов.
- Random — случайное распределение.
- Consistent Hashing — используется в распределённых системах для устойчивого распределения ключей.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Массив (array) — это структура данных, которая представляет собой фиксированную последовательность элементов одного типа. Все элементы массива размещены в памяти последовательно и имеют одинаковый тип. Используются для хранения коллекций данных, где количество элементов заранее известно и фиксировано.
Размер массива задается при его объявлении и не может изменяться во время выполнения программы.
Все элементы массива имеют один и тот же тип.
Элементы массива хранятся последовательно в памяти, что обеспечивает быстрый доступ к любому элементу по индексу.
Массивы объявляются с указанием типа элементов и фиксированного размера:
var arr [5]int
Могут быть инициализированы при объявлении:
arr := [5]int{1, 2, 3, 4, 5}
Можно также инициализировать массив частично, оставив остальные элементы равными нулям:
arr := [5]int{1, 2}
Осуществляется с использованием индексов, начиная с 0:
fmt.Println(arr[0]) // 1
arr[1] = 10
fmt.Println(arr[1]) // 10
Фиксирована и задается при его объявлении. Ее можно получить с помощью функции
len
:fmt.Println(len(arr)) // 5
При присваивании одного массива другому копируются все элементы:
arr1 := [5]int{1, 2, 3, 4, 5}
arr2 := arr1
arr2[0] = 10
fmt.Println(arr1) // [1 2 3 4 5]
fmt.Println(arr2) // [10 2 3 4 5]
Копируется весь массив:
func modifyArray(a [5]int) {
a[0] = 10
}
arr := [5]int{1, 2, 3, 4, 5}
modifyArray(arr)
fmt.Println(arr) // [1 2 3 4 5]
С помощью оператора
==
, если они имеют одинаковую длину и тип элементов:arr1 := [3]int{1, 2, 3}
arr2 := [3]int{1, 2, 3}
arr3 := [3]int{4, 5, 6}
fmt.Println(arr1 == arr2) // true
fmt.Println(arr1 == arr3) // false
Пример
package main
import (
"fmt"
)
func main() {
// Объявление и инициализация массива
arr := [5]int{1, 2, 3, 4, 5}
// Доступ к элементам
fmt.Println("First element:", arr[0]) // First element: 1
// Изменение элементов
arr[1] = 10
fmt.Println("Modified array:", arr) // Modified array: [1 10 3 4 5]
// Длина массива
fmt.Println("Length of array:", len(arr)) // Length of array: 5
// Копирование массива
arr2 := arr
arr2[0] = 20
fmt.Println("Original array:", arr) // Original array: [1 10 3 4 5]
fmt.Println("Copied array:", arr2) // Copied array: [20 10 3 4 5]
// Передача массива в функцию
modifyArray(arr)
fmt.Println("Array after modifyArray call:", arr) // Array after modifyArray call: [1 10 3 4 5]
}
func modifyArray(a [5]int) {
a[0] = 10
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Несмотря на свои многие преимущества, такие как простота, скорость и эффективность в работе с многопоточностью, имеет ряд недостатков, которые могут повлиять на выбор языка для конкретных проектов или задач.
До введения дженериков, одним из основных недостатков языка было отсутствие поддержки дженериков, что приводило к тому, что разработчики должны были использовать интерфейсы и тип
interface{}
для создания функций и структур, работающих с различными типами данных. Это могло привести к ухудшению производительности и увеличению сложности кода.Управление памятью осуществляется сборщиком мусора, который, хоть и очень эффективен, может быть непредсказуемым в плане времени выполнения. Это может быть критично для приложений с жёсткими требованиями к времени отклика, таких как высокопроизводительные игры или реалтаймовые системы.
Поскольку типы интерфейсов проверяются во время выполнения, а не во время компиляции, может возникнуть проблема с отладкой ошибок, связанных с типами данных. Кроме того, использование интерфейсов может привести к дополнительным накладным расходам на производительность.
Неизменяемы, что может привести к неэффективному использованию памяти и времени процессора при частой модификации строк, например, в циклах или больших объёмах текстовых данных.
До введения модулей в Go 1.11 управление зависимостями было сложным и могло привести к конфликтам и трудностям в сопровождении кода. Хотя модули значительно улучшили ситуацию, система версионирования и управления зависимостями в Go все еще может быть не такой гибкой, как в некоторых других языках.
Такие ошибки, как работа с нулевыми указателями, остаются довольно распространенными в Go, поскольку язык обладает автоматическим разыменованием указателей, что может привести к панике в рантайме.
Стремится быть простым и эффективным языком, что иногда ведет к отсутствию поддержки некоторых более сложных или специализированных функций, доступных в других языках, таких как метапрограммирование, генерики (до Go 1.18) или тонкая настройка управления памятью.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
defer реализует концепт отложенного выполнения — аналог "гарантированного освобождения ресурса" или финализации.
Это полезно для:
- Закрытия файлов или соединений.
- Освобождения ресурсов.
- Логирования.
- Обработки ошибок в recover.
Фактически defer помогает имитировать поведение finally из других языков.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Таймауты (
timeout
) в HTTP-запросах предотвращают зависание приложения при медленных или недоступных серверах. Они позволяют ограничить время ожидания ответа, чтобы избежать бесконечного ожидания и высвободить ресурсы.В Golang таймауты можно устанавливать на разных уровнях:
Timeout для всего запроса (включает подключение, отправку и получение данных)
client := &http.Client{
Timeout: 5 * time.Second, // Запрос не может длиться дольше 5 секунд
}
Таймаут на установку соединения
transport := &http.Transport{
DialContext: (&net.Dialer{
Timeout: 2 * time.Second, // 2 секунды на подключение
}).DialContext,
}
client := &http.Client{Transport: transport}
Таймаут на чтение и запись
transport := &http.Transport{
ResponseHeaderTimeout: 3 * time.Second, // 3 секунды на заголовки
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это интерфейс, который не содержит методов. Поскольку интерфейсы в Go определяют поведение, которое тип должен реализовать, пустой интерфейс, не имеющий методов, автоматически реализуется всеми типами. Это делает пустой интерфейс универсальным контейнером для значений любого типа.
Пустой интерфейс может содержать значение любого типа, потому что все типы в Go автоматически реализуют пустой интерфейс.
Пустой интерфейс широко используется для создания обобщенных (generic) структур данных, функций и методов, которые могут работать с данными любых типов.
Пустой интерфейс может использоваться для хранения значений различных типов в одной переменной.
package main
import "fmt"
func main() {
var i interface{}
i = 42
fmt.Println(i) // Output: 42
i = "hello"
fmt.Println(i) // Output: hello
i = true
fmt.Println(i) // Output: true
}
Пустой интерфейс позволяет создавать функции, которые могут принимать параметры любого типа.
package main
import "fmt"
func printValue(v interface{}) {
fmt.Println(v)
}
func main() {
printValue(42)
printValue("hello")
printValue(true)
}
Пустой интерфейс используется для создания структур данных, которые могут хранить значения различных типов.
package main
import "fmt"
func main() {
var values []interface{}
values = append(values, 42, "hello", true)
for _, v := range values {
fmt.Println(v)
}
}
Пустой интерфейс используется для обработки данных различных типов, например, при парсинге JSON.
package main
import (
"encoding/json"
"fmt"
)
func main() {
jsonData := `{"name": "Alice", "age": 30}`
var result map[string]interface{}
json.Unmarshal([]byte(jsonData), &result)
fmt.Println(result)
}
При работе с пустым интерфейсом часто возникает необходимость проверить тип хранимого значения и привести его к конкретному типу. Это можно сделать с помощью утверждения типа (
type assertion
) или конструкции switch
.Утверждение типа позволяет проверить и преобразовать значение пустого интерфейса к конкретному типу.
package main
import "fmt"
func main() {
var i interface{} = "hello"
s, ok := i.(string)
if ok {
fmt.Println("String:", s) // Output: String: hello
} else {
fmt.Println("Not a string")
}
n, ok := i.(int)
if ok {
fmt.Println("Integer:", n)
} else {
fmt.Println("Not an integer")
}
}
Конструкция
switch
позволяет обрабатывать значения различных типов, хранящиеся в пустом интерфейсе.package main
import "fmt"
func printType(i interface{}) {
switch v := i.(type) {
case string:
fmt.Println("String:", v)
case int:
fmt.Println("Integer:", v)
case bool:
fmt.Println("Boolean:", v)
default:
fmt.Printf("Unknown type: %T\n", v)
}
}
func main() {
printType("hello") // Output: String: hello
printType(42) // Output: Integer: 42
printType(true) // Output: Boolean: true
printType(3.14) // Output: Unknown type: float64
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Builder — это порождающий паттерн, позволяющий создавать сложные объекты пошагово, контролируя процесс построения.
Он особенно полезен, когда объект:
- имеет много опциональных параметров,
- требует разной конфигурации при создании,
- должен быть иммутабельным после построения.
Пример: создание HTTP-запроса или SQL-запроса с цепочкой .setX().setY().build().
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM