Интеграционные тесты — это тесты, которые проверяют взаимодействие между различными компонентами системы. В отличие от юнит-тестов, которые фокусируются на проверке отдельных функций или методов, интеграционные проверяют, как компоненты работают вместе, обеспечивая целостность и корректность всей системы.
Интеграционные тесты помогают убедиться, что различные части системы правильно взаимодействуют друг с другом. Это важно для выявления проблем на границах между компонентами, которые могут не проявиться в юнит-тестах.
Такие тесты помогают обнаруживать ошибки, возникающие при объединении модулей. Это могут быть проблемы с форматами данных, неправильное использование интерфейсов и другие ошибки, связанные с интеграцией.
Интеграционные тесты могут включать сценарии, приближенные к реальным условиям эксплуатации системы, что позволяет убедиться, что система работает правильно в реальной среде.
Интеграционные тесты могут требовать наличия нескольких зависимостей, таких как база данных, внешние API или другие сервисы. Настройте тестовую среду, которая будет эмулировать реальную среду.
Рассмотрим пример интеграционного теста для приложения, которое работает с базой данных. В этом примере мы будем тестировать функции создания и получения пользователя.
// main.go
package main
import (
"database/sql"
_ "github.com/mattn/go-sqlite3"
"log"
)
type User struct {
ID int
Name string
}
func CreateUser(db *sql.DB, name string) (int, error) {
res, err := db.Exec("INSERT INTO users(name) VALUES(?)", name)
if err != nil {
return 0, err
}
id, err := res.LastInsertId()
if err != nil {
return 0, err
}
return int(id), nil
}
func GetUser(db *sql.DB, id int) (User, error) {
var user User
err := db.QueryRow("SELECT id, name FROM users WHERE id = ?", id).Scan(&user.ID, &user.Name)
if err != nil {
return user, err
}
return user, nil
}
func main() {
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
log.Fatal(err)
}
defer db.Close()
_, err = db.Exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
if err != nil {
log.Fatal(err)
}
}
// main_test.go
package main
import (
"database/sql"
"testing"
_ "github.com/mattn/go-sqlite3"
)
func setupTestDB(t *testing.T) *sql.DB {
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
t.Fatal(err)
}
_, err = db.Exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
if err != nil {
t.Fatal(err)
}
return db
}
func TestCreateAndGetUser(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
// Создание пользователя
userID, err := CreateUser(db, "John Doe")
if err != nil {
t.Fatalf("Failed to create user: %v", err)
}
// Получение пользователя
user, err := GetUser(db, userID)
if err != nil {
t.Fatalf("Failed to get user: %v", err)
}
// Проверка результатов
if user.Name != "John Doe" {
t.Errorf("Expected name to be 'John Doe', got %s", user.Name)
}
}
Интеграционные тесты проверяют, как различные компоненты системы работают вместе. Они помогают выявлять ошибки на границах между модулями и обеспечивают целостность системы. Пишутся аналогично юнит-тестам, но включают взаимодействие между компонентами и часто требуют настройки тестовой среды.
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какое ключевое слово в Go используется для создания именованных типов, которые могут быть основой для методов?
Anonymous Quiz
6%
new
59%
type
21%
struct
14%
interface
Что будет, если подставить переменную в массив ?
Спросят с вероятностью 8%
Подстановка переменной в массив происходит путем присвоения значения элементу массива. Они имеют фиксированный размер, который задается при их объявлении, и все элементы массива должны быть одного типа.
Пример использования:
В этом примере:
1️⃣Мы объявляем массив
2️⃣Создаем переменную
3️⃣Подставляем значение переменной
4️⃣Выводим массив, чтобы увидеть результат.
Изменение элементов
Массивы индексируются с нуля, и доступ к элементам массива осуществляется с использованием квадратных скобок
Переменные также могут использоваться для индексов массива:
Массивы могут содержать структуры. Рассмотрим пример с массивом структур:
Подстановка переменной в массив происходит через присваивание значения элементу массива по индексу. Массивы имеют фиксированный размер и могут содержать элементы одного типа. Можно также использовать переменные для индексов массива и работать с массивами структур.
👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент
🔐 База собесов | 🔐 База тестовых
Спросят с вероятностью 8%
Подстановка переменной в массив происходит путем присвоения значения элементу массива. Они имеют фиксированный размер, который задается при их объявлении, и все элементы массива должны быть одного типа.
Пример использования:
package main
import "fmt"
func main() {
// Объявление и инициализация массива
var arr [5]int
// Переменная, значение которой будет подставлено в массив
value := 10
// Подставляем значение переменной в массив
arr[0] = value
// Вывод массива
fmt.Println(arr) // Output: [10 0 0 0 0]
}
В этом примере:
1️⃣Мы объявляем массив
arr
из 5 целых чисел (int
), изначально инициализированных нулями.2️⃣Создаем переменную
value
и присваиваем ей значение 10.3️⃣Подставляем значение переменной
value
в первый элемент массива arr
с индексом 0.4️⃣Выводим массив, чтобы увидеть результат.
Изменение элементов
Массивы индексируются с нуля, и доступ к элементам массива осуществляется с использованием квадратных скобок
[]
. Можно изменять значения элементов массива, присваивая им новые значения через соответствующие индексы:package main
import "fmt"
func main() {
// Объявление и инициализация массива
arr := [5]int{1, 2, 3, 4, 5}
// Переменные для подстановки
value1 := 10
value2 := 20
// Подстановка значений переменных в массив
arr[1] = value1
arr[3] = value2
// Вывод массива
fmt.Println(arr) // Output: [1 10 3 20 5]
}
Переменные также могут использоваться для индексов массива:
package main
import "fmt"
func main() {
// Объявление и инициализация массива
arr := [5]int{1, 2, 3, 4, 5}
// Переменные для индексов
idx1 := 2
idx2 := 4
// Подстановка значений в массив по индексам
arr[idx1] = 30
arr[idx2] = 50
// Вывод массива
fmt.Println(arr) // Output: [1 2 30 4 50]
}
Массивы могут содержать структуры. Рассмотрим пример с массивом структур:
package main
import "fmt"
// Определение структуры
type Person struct {
Name string
Age int
}
func main() {
// Объявление массива структур
var people [3]Person
// Переменная структуры
p := Person{Name: "Alice", Age: 30}
// Подстановка структуры в массив
people[0] = p
// Прямое присвоение структуры в массив
people[1] = Person{Name: "Bob", Age: 25}
people[2] = Person{Name: "Charlie", Age: 35}
// Вывод массива структур
fmt.Println(people)
// Output: [{Alice 30} {Bob 25} {Charlie 35}]
}
Подстановка переменной в массив происходит через присваивание значения элементу массива по индексу. Массивы имеют фиксированный размер и могут содержать элементы одного типа. Можно также использовать переменные для индексов массива и работать с массивами структур.
👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент
🔐 База собесов | 🔐 База тестовых
🤔 Какой метод используется для сериализации структуры в JSON формат?
Anonymous Quiz
17%
Unmarshal
5%
Encode
72%
Marshal
6%
Serialize
Стандартный пакет testing предоставляет инструменты для написания и выполнения юнит-тестов. Давайте рассмотрим, как писать юнит-тесты с использованием этого пакета.
_test.go
. Это позволяет инструменту тестирования Go автоматически распознавать их как тестовые файлы.Test
и принимать один аргумент типа *testing.T
. Это соглашение позволяет инструменту тестирования Go идентифицировать их как тестовые функции.package main
func Add(a, b int) int {
return a + b
}
// main_test.go
package main
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
expected := 5
if result != expected {
t.Errorf("Add(2, 3) = %d; want %d", result, expected)
}
}
Чтобы это сделать, выполните команду
go test
в терминале. Эта команда автоматически найдет и выполнит все тесты в текущем пакете: $ go test Тестирование с таблицей (table-driven tests): Это подход, при котором вы пишете тесты в виде таблицы входных и ожидаемых значений. Это улучшает читаемость и позволяет легко добавлять новые тестовые случаи.
package main
import "testing"
func TestAdd(t *testing.T) {
tests := []struct {
a, b, expected int
}{
{1, 2, 3},
{0, 0, 0},
{-1, -1, -2},
{2, 2, 4},
}
for _, tt := range tests {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, result, tt.expected)
}
}
}
Бенчмаркинг: Для измерения производительности кода используйте функции, начинающиеся с
Benchmark
и принимающие *testing.B
.package main
import "testing"
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(2, 3)
}
}
Примерные тесты (Example Tests)
package main
import "fmt"
func ExampleAdd() {
fmt.Println(Add(2, 3))
// Output: 5
}
TestAdd
для тестирования функции Add
.Запуск тестов
-cover
:$ go test -coverЮнит-тесты пишутся в файлах с суффиксом
_test.go
и выполняются с помощью команды go test
. Они проверяют отдельные функции или методы, чтобы убедиться, что они работают корректно. Пакет testing
предоставляет все необходимые инструменты для написания и выполнения тестов, бенчмарков и примерных тестов.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какое ключевое слово используется для импорта пакета в Go?
Anonymous Quiz
3%
include
2%
require
94%
import
1%
use
Для копирования срезов (slice) можно использовать встроенную функцию
copy
. Эта функция копирует элементы из одного среза в другой, обеспечивая корректное и эффективное копирование данных. Принимает два аргумента: целевой срез и исходный срез. Она копирует элементы из исходного среза в целевой и возвращает количество скопированных элементов. В этом примере исходный срез
src
копируется в целевой срез dst
. Размер целевого среза должен быть равен или больше размера исходного среза, чтобы все элементы могли быть скопированы.package main
import "fmt"
func main() {
// Исходный срез
src := []int{1, 2, 3, 4, 5}
// Целевой срез, который должен иметь достаточно места для копирования элементов
dst := make([]int, len(src))
// Копирование среза
n := copy(dst, src)
// Вывод результатов
fmt.Println("Исходный срез:", src) // Output: Исходный срез: [1 2 3 4 5]
fmt.Println("Целевой срез:", dst) // Output: Целевой срез: [1 2 3 4 5]
fmt.Println("Количество скопированных элементов:", n) // Output: Количество скопированных элементов: 5
}
Частичное копирование: Если целевой срез меньше исходного,
copy
скопирует только столько элементов, сколько поместится в целевой срез.package main
import "fmt"
func main() {
// Исходный срез
src := []int{1, 2, 3, 4, 5}
// Целевой срез меньшего размера
dst := make([]int, 3)
// Копирование среза
n := copy(dst, src)
// Вывод результатов
fmt.Println("Исходный срез:", src) // Output: Исходный срез: [1 2 3 4 5]
fmt.Println("Целевой срез:", dst) // Output: Целевой срез: [1 2 3]
fmt.Println("Количество скопированных элементов:", n) // Output: Количество скопированных элементов: 3
}
Копирование разных типов: Функция
copy
работает только с срезами одного типа. Если вы попытаетесь копировать элементы между срезами разных типов, это приведет к ошибке компиляции.package main
import "fmt"
func main() {
// Исходный срез
src := []int{1, 2, 3, 4, 5}
// Целевой срез
dst := make([]int, 2)
// Копирование части среза
n := copy(dst, src[1:3])
// Вывод результатов
fmt.Println("Исходный срез:", src) // Output: Исходный срез: [1 2 3 4 5]
fmt.Println("Целевой срез:", dst) // Output: Целевой срез: [2 3]
fmt.Println("Количество скопированных элементов:", n) // Output: Количество скопированных элементов: 2
}
Глубокое копирование срезов с вложенными структурами: Если он содержит сложные структуры данных, такие как структуры или срезы с указателями, вам может понадобиться реализовать глубокое копирование, чтобы копировать не только сами элементы, но и данные, на которые они ссылаются.
package main
import "fmt"
type Item struct {
Value int
}
func main() {
// Исходный срез
src := []Item{{1}, {2}, {3}}
// Целевой срез
dst := make([]Item, len(src))
// Глубокое копирование
for i, item := range src {
dst[i] = Item{Value: item.Value}
}
// Изменение исходного среза не влияет на целевой срез
src[0].Value = 100
// Вывод результатов
fmt.Println("Исходный срез:", src) // Output: Исходный срез: [{100} {2} {3}]
fmt.Println("Целевой срез:", dst) // Output: Целевой срез: [{1} {2} {3}]
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какой тип используется для представления динамических массивов в Go?
Anonymous Quiz
6%
array
3%
list
86%
slice
6%
map
Моки (mocks) — это объекты, которые имитируют поведение реальных объектов в тестах. Они используются для изоляции тестируемого кода от зависимостей, таких как базы данных, внешние API, файловая система и другие сервисы. Моки позволяют сосредоточиться на тестировании конкретной части кода без необходимости задействовать реальные ресурсы, что делает тесты более быстрыми и предсказуемыми.
Для создания и использования их часто используют специальные библиотеки, такие как
gomock
или testify/mock
. Рассмотрим пример использования библиотеки testify/mock
.Установка библиотеки testify
go get github.com/stretchr/testify
// api.go
package main
type APIClient interface {
FetchData(endpoint string) (string, error)
}
func GetData(client APIClient, endpoint string) (string, error) {
data, err := client.FetchData(endpoint)
if err != nil {
return "", err
}
return data, nil
}
Создадим мок для интерфейса
APIClient
с использованием библиотеки testify/mock
.// api_test.go
package main
import (
"errors"
"testing"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/assert"
)
// MockAPIClient is a mock type for the APIClient interface
type MockAPIClient struct {
mock.Mock
}
func (m *MockAPIClient) FetchData(endpoint string) (string, error) {
args := m.Called(endpoint)
return args.String(0), args.Error(1)
}
func TestGetData(t *testing.T) {
// Создание мока
mockClient := new(MockAPIClient)
// Настройка мока для возврата данных
mockClient.On("FetchData", "test-endpoint").Return("mocked data", nil)
// Вызов тестируемой функции
data, err := GetData(mockClient, "test-endpoint")
// Проверка результатов
assert.NoError(t, err)
assert.Equal(t, "mocked data", data)
// Проверка вызова мока
mockClient.AssertExpectations(t)
}
func TestGetDataWithError(t *testing.T) {
// Создание мока
mockClient := new(MockAPIClient)
// Настройка мока для возврата ошибки
mockClient.On("FetchData", "error-endpoint").Return("", errors.New("fetch error"))
// Вызов тестируемой функции
data, err := GetData(mockClient, "error-endpoint")
// Проверка результатов
assert.Error(t, err)
assert.Equal(t, "", data)
// Проверка вызова мока
mockClient.AssertExpectations(t)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какой пакет используется для работы с регулярными выражениями в Go?
Anonymous Quiz
40%
regex
11%
reg
8%
re
42%
regexp
Оператор квадратные скобки
[]
, применяемый к строке, возвращает байт (тип byte
), представляющий символ в данной позиции строки. Строки являются неизменяемыми последовательностями байтов, и доступ к отдельным символам строки осуществляется через индексацию.package main
import "fmt"
func main() {
str := "hello"
// Получение первого символа строки
firstChar := str[0]
// Вывод значения байта
fmt.Printf("Первый символ: %c\n", firstChar) // Output: Первый символ: h
// Вывод байтового значения
fmt.Printf("Байтовое значение: %d\n", firstChar) // Output: Байтовое значение: 104
}
В этом примере
str[0]
возвращает байт, представляющий первый символ строки "hello"
, который является символом 'h'
и имеет байтовое значение 104.str[i]
возвращает байт, соответствующий i
-тому индексу.[:]
, который также возвращает новую строку.substr := str[1:4]
fmt.Println(substr) // Output: ell
Так как строки в Go являются последовательностями байтов, они могут содержать символы Unicode, которые могут занимать более одного байта. Для правильной обработки Unicode символов нужно использовать пакет
unicode/utf8
.package main
import (
"fmt"
"unicode/utf8"
)
func main() {
str := "Привет"
// Получение первого символа Unicode
runeValue, size := utf8.DecodeRuneInString(str)
fmt.Printf("Первый символ: %c\n", runeValue) // Output: Первый символ: П
fmt.Printf("Размер в байтах: %d\n", size) // Output: Размер в байтах: 2
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какой метод интерфейса io.Reader используется для чтения данных?
Anonymous Quiz
69%
Read
28%
Scan
3%
Fetch
0%
Receive
Карты (maps) используются для хранения пар ключ-значение. Вставка элементов — это довольно простая операция. Давайте рассмотрим, как это делается, и объясним каждый шаг.
Прежде чем вставить элементы в карту, необходимо ее создать. Карты в Go могут быть созданы с использованием встроенной функции
make
или литерала карты.Создание карты с использованием
make
: Здесь мы создаем карту myMap
, которая использует строки в качестве ключей и целые числа в качестве значений.myMap := make(map[string]int)
Создание карты с использованием литерала карты: В этом примере мы создаем карту и сразу инициализируем ее несколькими значениями.
myMap := map[string]int{
"one": 1,
"two": 2,
}
Вставка элементов в карту: В этом примере мы создаем пустую карту
myMap
и вставляем в нее три пары ключ-значение. Ключи — это строки ("one", "two", "three"), а значения — целые числа (1, 2, 3). Когда мы присваиваем значение конкретному ключу (myMap["one"] = 1
), Go автоматически добавляет эту пару в карту.package main
import "fmt"
func main() {
// Создание карты
myMap := make(map[string]int)
// Вставка элементов
myMap["one"] = 1
myMap["two"] = 2
myMap["three"] = 3
// Вывод карты
fmt.Println(myMap)
}
Обновление элементов карты: Здесь значение ключа "two" обновляется с 2 на 22, а новый ключ "three" добавляется со значением 3.
func main() {
// Создание и инициализация карты
myMap := map[string]int{
"one": 1,
"two": 2,
}
// Обновление значения существующего ключа
myMap["two"] = 22
// Добавление нового ключа
myMap["three"] = 3
// Вывод карты
fmt.Println(myMap)
}
Для проверки, существует ли ключ в карте, используется конструкция с двумя возвращаемыми значениями:
value, exists := myMap["two"]
if exists {
fmt.Println("Key 'two' exists with value:", value)
} else {
fmt.Println("Key 'two' does not exist")
}
Для удаления элемента из карты используется встроенная функция
delete
:delete(myMap, "two")
fmt.Println("After deletion:", myMap)
package main
import "fmt"
func main() {
// Создание карты
myMap := make(map[string]int)
// Вставка элементов
myMap["one"] = 1
myMap["two"] = 2
myMap["three"] = 3
// Обновление элемента
myMap["two"] = 22
// Проверка наличия ключа
value, exists := myMap["two"]
if exists {
fmt.Println("Key 'two' exists with value:", value)
} else {
fmt.Println("Key 'two' does not exist")
}
// Удаление элемента
delete(myMap, "two")
fmt.Println("After deletion:", myMap)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какое значение имеет переменная m["key"] после удаления ключа в Go?
Anonymous Quiz
26%
0
1%
1
58%
nil
15%
panic
Для ключей в картах (maps) можно использовать типы, которые удовлетворяют определенным требованиям. Ключи должны быть сравнимыми, то есть поддерживать операторы сравнения (
==
и !=
). Это означает, что типы, используемые в качестве ключей, должны иметь возможность быть сравнены на равенство. В Go это включает следующие типы:map[int]string
, map[int64]int
map[string]int
map[bool]string
map[*int]string
map[chan int]string
map[interface{}]string
, но конкретные значения, хранимые в интерфейсах, должны быть сравнимы.map[struct{a int; b string}]int
Использование целых чисел в качестве ключей:
package main
import "fmt"
func main() {
intMap := make(map[int]string)
intMap[1] = "one"
intMap[2] = "two"
fmt.Println(intMap) // Output: map[1:one 2:two]
}
Использование строк в качестве ключей:
package main
import "fmt"
func main() {
stringMap := make(map[string]int)
stringMap["one"] = 1
stringMap["two"] = 2
fmt.Println(stringMap) // Output: map[one:1 two:2]
}
Использование структур в качестве ключей:
package main
import "fmt"
type Person struct {
FirstName string
LastName string
}
func main() {
structMap := make(map[Person]int)
structMap[Person{"John", "Doe"}] = 1
structMap[Person{"Jane", "Doe"}] = 2
fmt.Println(structMap) // Output: map[{John Doe}:1 {Jane Doe}:2]
}
Использование указателей в качестве ключей:
package main
import "fmt"
func main() {
int1 := 1
int2 := 2
ptrMap := make(map[*int]string)
ptrMap[&int1] = "one"
ptrMap[&int2] = "two"
fmt.Println(ptrMap) // Output: map[0x...:one 0x...:two]
}
Ограничения
float32
и float64
могут быть использованы, их использование в качестве ключей в картах не рекомендуется из-за проблем с точностью при сравнении плавающих точек.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какое значение имеет переменная после попытки приведения типа, если приведение не удалось?
Anonymous Quiz
31%
Значение по умолчанию для типа
22%
Ошибка времени выполнения
24%
Значение переменной до приведения
24%
nil
Хэш-коллизия — это случай, когда два разных входных значения генерируют один и тот же хэш-код. Это происходит из-за ограниченного числа возможных хэш-значений, которые могут привести к повторениям при большом количестве входных данных.
Принимает входные данные (например, строку или целое число) и возвращает фиксированное значение, которое обычно используется для индексирования массива или другого контейнера данных. Однако, поскольку количество возможных входных данных потенциально бесконечно, а количество возможных выходных значений хэш-функции ограничено, неизбежно возникают ситуации, когда разные входные данные производят одинаковое хэш-значение. Это и называется хэш-коллизией. Представим, что у нас есть простая хэш-функция для строк, которая возвращает длину строки в качестве хэш-значения. Для строк "cat" и "dog" хэш-значение будет одинаковым (3), что приводит к коллизии.
Существует несколько методов для управления хэш-коллизиями. Два наиболее распространенных метода — это цепочки (chaining) и открытая адресация (open addressing).
В этом методе каждая ячейка хэш-таблицы содержит указатель на список (например, связанный список) всех элементов, которые имеют одно и то же хэш-значение. Когда происходит коллизия, новый элемент просто добавляется в этот список.
type Entry struct {
key string
value int
next *Entry
}
type HashMap struct {
buckets []*Entry
}
func (m *HashMap) Put(key string, value int) {
index := hash(key) % len(m.buckets)
entry := m.buckets[index]
for entry != nil {
if entry.key == key {
entry.value = value
return
}
entry = entry.next
}
m.buckets[index] = &Entry{key: key, value: value, next: m.buckets[index]}
}
func hash(key string) int {
hash := 0
for _, char := range key {
hash += int(char)
}
return hash
}
В этом методе все элементы хранятся непосредственно в хэш-таблице. Если ячейка, определенная хэш-функцией, уже занята, используется альтернативная стратегия для нахождения следующей доступной ячейки. Наиболее распространенные стратегии включают линейное пробирование (linear probing), квадратичное пробирование (quadratic probing) и двойное хеширование (double hashing).
type HashMap struct {
keys []string
values []int
size int
}
func (m *HashMap) Put(key string, value int) {
index := hash(key) % len(m.keys)
for m.keys[index] != "" {
if m.keys[index] == key {
m.values[index] = value
return
}
index = (index + 1) % len(m.keys)
}
m.keys[index] = key
m.values[index] = value
m.size++
}
func hash(key string) int {
hash := 0
for _, char := range key {
hash += int(char)
}
return hash
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как в Go реализуется интерфейс для пользовательского типа?
Anonymous Quiz
13%
Объявить тип и явно указать интерфейсы
67%
Просто объявить методы интерфейса для типа
4%
Использовать ключевое слово implements
16%
Создать структуру, содержащую интерфейс
HTTP/3 — это последняя версия протокола HTTP, предназначенная для передачи данных в интернете. Эта версия включает несколько значительных улучшений по сравнению с предыдущими версиями, в частности благодаря использованию протокола QUIC.
QUIC (Quick UDP Internet Connections) разработан Google и работает поверх UDP, а не TCP. Обеспечить более быструю и надежную передачу данных, улучшить производительность и сократить задержки.
QUIC значительно сокращает время на установление соединения по сравнению с TCP. Благодаря интеграции с TLS, процесс безопасности и установка соединения происходят одновременно.
В HTTP/3 каждый поток данных передается независимо, что предотвращает блокировку одного потока из-за потери пакетов в другом потоке, что является недостатком HTTP/2.
HTTP/3 позволяет отправлять несколько запросов и получать ответы через одно соединение одновременно без блокировок, что ускоряет передачу данных и повышает эффективность использования соединений.
Новый механизм сжатия заголовков, улучшенный по сравнению с HPACK в HTTP/2, который учитывает особенности QUIC и уменьшает задержки.
Как и в HTTP/2, HTTP/3 поддерживает серверное push-сообщение, позволяя серверу отправлять данные клиенту до их запроса, что ускоряет загрузку страниц.
Быстрое установление соединений и устойчивость к потерям пакетов делают HTTP/3 значительно быстрее, особенно в условиях ненадежных сетей.
Независимая передача потоков данных и улучшенное мультиплексирование снижают задержки при загрузке страниц и передачи данных.
Улучшенная устойчивость к сетевым сбоям и потерям пакетов делает соединения более надежными и стабильными.
Поскольку HTTP/3 использует QUIC и UDP, а не TCP, некоторые старые или ограниченные сети и устройства могут не поддерживать этот протокол.
Внедрение и поддержка HTTP/3 требуют новых знаний и навыков, что может усложнить разработку и администрирование.
Переход на HTTP/3 может требовать обновления серверного оборудования и программного обеспечения для полной поддержки нового протокола.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какой интерфейс должен реализовывать тип, чтобы его можно было использовать в качестве ключа в map?
Anonymous Quiz
25%
hashable
65%
comparable
8%
keyable
3%
equatable
Это технология, используемая для изменения сетевых адресов в заголовках пакетов данных, которые проходят через маршрутизатор или межсетевой экран. Она позволяет нескольким устройствам в локальной сети использовать один и тот же публичный IP-адрес для выхода в интернет.
Фиксированный сопоставление: Один внутренний IP-адрес сопоставляется с одним внешним IP-адресом. Используется, когда необходимо, чтобы устройство в локальной сети всегда было доступно под одним и тем же публичным IP-адресом. Например Веб-сервер, который должен быть доступен из интернета под фиксированным IP-адресом.
Внутренние IP-адреса сопоставляются с пулом внешних IP-адресов. Когда внутреннее устройство инициирует соединение с интернетом, ему временно присваивается один из доступных внешних IP-адресов. Например, локальная сеть с большим количеством устройств, где не требуется фиксированный внешний IP-адрес для каждого устройства.
Несколько внутренних IP-адресов могут использовать один внешний IP-адрес, но различаются по номерам портов. Каждый внутренний IP-адрес и порт сопоставляется с уникальным внешним портом. Например, Домашние или офисные сети, где множество устройств выходят в интернет через один публичный IP-адрес.
IPv4-адресов недостаточно для всех устройств, и NAT позволяет использовать один публичный IP-адрес для множества устройств.
Внутренние IP-адреса не видны извне, что усложняет потенциальным злоумышленникам попытки атак на внутренние устройства.
NAT позволяет администрировать и контролировать сетевой трафик, предоставляя возможности для управления доступом и приоритизацией трафика.
Когда устройство в локальной сети (например, компьютер с IP-адресом 192.168.1.10) инициирует соединение с устройством в интернете, NAT изменяет исходящий IP-адрес и порт на внешний IP-адрес маршрутизатора и уникальный номер порта.
Когда ответный пакет возвращается, NAT использует таблицу сопоставлений, чтобы определить, к какому внутреннему устройству направить пакет, и изменяет внешний IP-адрес и порт обратно на внутренний IP-адрес и порт.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM