DNK_C_C++_Go_Rust
45 subscribers
14 photos
45 links
DNK - дневник кодера С и С++
Download Telegram
#523_ADM_DvOp_NTWK_TP

Типы DNS записей и их значения.

Система DNS (Domain Name System)представляет собой базу данных, содержащую информацию о доменах и соответствующих им IP-адресах.
Каждое доменное имя ассоциируется с различными записями DNS, называемыми ресурсными записями (Resource Records).
Эти записи предоставляют различную информацию о домене, такую как IP-адреса, почтовые серверы, направления переадресаций и многое другое.


Распространенные типы DNS-записей и их назначения:

1. A (Address Record)
Назначение: Связывает доменное имя с IPv4-адресом.
Формат: <доменное имя> → <IPv4-адрес>
Пример: www.example.com IN A 192.0.2.1

2. AAAA (Quad-A Address Record)
Назначение: Связывает доменное имя с IPv6-адресом.
Формат: <доменное имя> → <IPv6-адрес>
Пример:
www.example.com IN AAAA 2001:DB8::1

3. CNAME (Canonical Name Record)
Назначение: Создает псевдоним (алиас) другого доменного имени. Все запросы, направленные на псевдоним, будут автоматически перенаправлены на основное доменное имя.
Формат: <псевдоним> → <основное доменное имя>
Пример: blog.example.com IN CNAME www.example.com

4. MX (Mail Exchanger Record)
Назначение: Определяет почтовые серверы, принимающие почту для указанного домена.
Формат: <доменное имя> → <почтовый сервер>
Пример:
example.com IN MX 10 mail.example.com

5. NS (Name Server Record)
Назначение: Указывает, какой DNS-сервер отвечает за доменную зону.
Формат: <доменная зона> → <сервер имен>
Пример:
example.com IN NS ns1.example.net

6. PTR (Pointer Record)
Назначение: Осуществляет обратное разрешение IP-адреса в доменное имя (reverse DNS lookup).
Формат: <IP-адрес> → <доменное имя>
Пример:
1.2.3.4.in-addr.arpa IN PTR server.example.com

7. SOA (Start of Authority Record)
Назначение: Содержит административную информацию о зоне DNS, включая контактную информацию администратора, серийный номер и таймеры повторных проверок.
Формат: <имя зоны> → <первичный мастер DNS>, <email администратора>, <серийный номер>, <тайминги>
Пример:
example.com IN SOA ns1.example.com admin.example.com (1 3600 600 86400 3600)

8. TXT (Text Record)
Назначение: Хранит произвольные текстовые данные, например, SPF-записи для подтверждения подлинности электронной почты, DKIM-подписи и другие служебные данные.
Формат: <доменное имя> → <текстовое значение>
Пример:
example.com IN TXT "v=spf1 include:_spf.google.com ~all"

9. SRV (Service Locator Record)
Назначение:
Определять местонахождение конкретных сервисов (например, SIP, LDAP, Jabber и т.д.) в пределах доменной зоны.
Формат: _service._protocol.<domain-name> → <приоритет вес port host>
Пример:
_sip._tcp.example.com IN SRV 0 5 5060 sipserver.example.com

10. CAA (Certification Authority Authorization Record)
Назначение:
Управляет полномочиями центров сертификации (CA) выдавать SSL/TLS сертификаты для определенного домена.
Формат: <доменное имя> → <флаг issue|issuewild|iodef> "<CA>" [";" options]
Пример:
example.com IN CAA 0 issue "letsencrypt.org"


Эти типы DNS-записей
обеспечивают надежное функционирование интернета путем перевода понятных человеку доменных имен в машиночитаемые IP-адреса и предоставления дополнительной информации о доменах, необходимой для правильной доставки писем, обеспечения безопасности и настройки сетевых сервисов.
#524_ADM_DvOp

Что делает команда docker prune ?

Команда docker system prune служит для очистки неиспользуемых объектов Docker, включая образы контейнеров, тома, сети и построенные кэшированные слои образов. По умолчанию эта команда удаляет следующие объекты:

Остаточные незадействованные образы контейнеров ("dangling images") — образы, которые больше не используются ни одним работающим контейнером.
Неиспользуемые тома ("volumes"), созданные ранее контейнерами, но теперь ненужные.
Удалённые несвязанные сети ("networks").
Построенный кэш сборки образа ("build cache"). Этот кэш помогает ускорить последующие операции сборки, но иногда занимает много места.

Опционально команду можно дополнить флагами, расширив её возможности удаления:

--all (-a) — удаление всех остановленных контейнеров, неиспользованных образов (не только dangling), а также всё остальное.
--force (-f) — принудительное выполнение команды без подтверждения.
--volumes (-v) — дополнительно удалить все неиспользуемые тома.

Пример полного удаления всего ненужного, включая объёмы:

docker system prune --all --volumes

Эта команда очистит систему от любых остаточных элементов Docker, освободив дополнительное пространство на диске.
#526_RUST_Vim

Плагины vim для работы c Rust.

Популярные плагины Vim для разработки на Rust, которые существенно упрощают процесс написания, тестирования и компиляции кода:

1. Rust.vim.
Описание: Официальный синтаксис и автозавершение для Rust.
Установка: Через менеджер плагинов, например Vundle, Vim-plug:
Plugin 'rust-lang/rust.vim'

Особенности: Улучшенный синтаксис, подсветка ошибок, поддержка формата Cargo.toml.


2. Vim-Racer.
Описание: Интеграция Racer для автодополнения Rust-кода.
Установка: Vundle, Vim-plug:
Plugin 'racer-rust/vim-racer'

Особенности: Быстрая автодополнение и навигация по коду, интегрирована с cargo build.

3. Ale.
Описание: Асинхронный Linter Engine для проверки кода на ошибки.
Установка: Vundle, Vim-plug:
Plugin 'dense-analysis/ale'

Особенности: Поддерживает интеграцию с clippy и rustfmt, проверка синтаксиса и стиля кода прямо в редакторе.


4. LanguageClient-vim.
Описание:
Клиент Language Server Protocol (LSP) для интеграции различных серверов анализа и автодополнения.
Установка: Vundle, Vim-plug:
Plugin 'autozygote/languageclient-vim'

Особенности: Позволяет подключаться к серверам вроде rls (Rust Language Server) для полноценного автодополнения, рефакторинга и навигации.


5. Easymotion.
Описание:
Ускорение перемещения по файлам с помощью комбинаций клавиш.
Установка: Vundle, Vim-plug:
Plugin 'easymotion/vim-easymotion'

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


6. YouCompleteMe.
Описание: Автодополнение для множества языков программирования, включая Rust.
Установка: Vundle, Vim-plug:
Plugin 'ycm-core/YouCompleteMe'

Особенности: Мощное автодополнение, требует предварительной сборки, но обеспечивает высокую производительность.


7. Vim-cargo.
Описание: Удобства для работы с Cargo (менеджером проектов Rust).
Установка: Vundle, Vim-plug:
Plugin 'sheerun/vim-cargo'

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


8. TabNine.
Описание:
Умное автодополнение на основе машинного обучения.
Установка: Vundle, Vim-plug:
Plugin 'tabnine/tabnine-vim'

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


Настройка минимального рабочего окружения:
Пример простого конфига Vim для комфортной работы с Rust-проектами:
set nocompatible                " Включаем режим совместимости
syntax on " Используем подсвечивание синтаксиса
filetype plugin indent on " Загружаем плагины для каждого типа файлов

call plug#begin('~/.vim/bundle')
Plug 'rust-lang/rust.vim' " Оффициальный плагин поддержки Rust
Plug 'dense-analysis/ale' " Линтер
Plug 'sheerun/vim-cargo' " Управление проектами через Cargo
Plug 'raimon49/vim-autoclose' " Автоматическое закрытие скобок и кавычек
call plug#end()

" Настройки ALE
let g:ale_fix_on_save = 1 " Исправлять стиль кода при сохранении
let g:ale_linters_explicit = 1 " Использовать конкретные линтеры
let g:ale_rust_cargo_check_command = ['cargo', '+nightly', 'check']

" Настройки RLS (если используете)
if executable('rls') || executable('cargo')
let g:LanguageClient_serverCommands = {
\ 'rust': ['rls'],
\ }
endif



Используя такие инструменты, можно значительно повысить продуктивность и удобство разработки на Rust в Vim.
#527_Vim_TLS

Как настроить Vim таким образом, чтобы при работе с различными языками использовались только определенные плагины, а какие-то плагины работали всегда и везде?

Для настройки Vim таким образом, чтобы отдельные плагины включались автоматически при открытии файлов определённых типов, а некоторые плагины загружались постоянно вне зависимости от типа файла, удобно воспользоваться механизмом автоматического запуска команд (autocmd) и менеджером пакетов, например vim-plug или встроенным пакетом системы самого Vim — packer.nvim.


Пример конфигурации с использованием менеджера vim-plug.
Предположим, мы хотим включить следующие плагины:
Всегда включены: подсветка синтаксиса (syntastic), работа с тегами (tagbar)
Включаются только при редактировании Python-файлов:
автоформатирование (autopep8), автодополнение (jedi-vim)
Включаются только при редактировании JavaScript-файлов: ESLint интеграция (vim-eslint), автодополнение для JSX/TSX (tern_for_vim)


Шаги:
Убедитесь, что у вас установлен менеджер пакетов, например vim-plug.

Для установки выполните команду внутри Vim:

:PlugInstall

Добавьте следующую конфигурацию в ваш .vimrc или аналогичный файл настроек:
" Всегда подгружаемые плагины
call plug#begin('~/.vim/plugs')

" Подсветка ошибок (работает везде)
Plug 'scrooloose/syntastic'

" Работа с Ctags (отображает структуру проекта)
Plug 'majutsushi/tagbar'

" Плагины по языку программирования

" Только для Python
au FileType python Plug 'nvie/vim-autpep8' | Plug 'davidhalter/jedi-vim'

" Только для JavaScript
au FileType javascript,jsx Plug 'vim-javascript/vim-jsx' | Plug 'mxw/vim-jsx'

" Общие команды для загрузки всех указанных плагинов
call plug#end()

Как работает эта настройка?
Сначала устанавливаются два глобальных плагина, которые будут активированы всегда: syntastic и tagbar.
Далее с помощью механизма автокоманд (FileType) указывается, что при открытии файлов Python и JavaScript подключаются дополнительные специфичные для этих языков инструменты.

Например, команда:
au FileType python Plug 'nvie/vim-autpep8' | Plug 'davidhalter/jedi-vim'

означает следующее: если тип текущего открытого файла определяется как python, тогда загрузятся плагины vim-autpep8 и jedi-vim.

Аналогично:

au FileType javascript,jsx Plug 'vim-javascript/vim-jsx' | Plug 'mxw/vim-jsx'

подключит плагины только при открытии файлов JavaScript и JSX.


Таким образом, нужные плагины будут грузиться динамически в зависимости от открываемого файла, а обязательные утилиты будут запускаться сразу при старте редактора.


Если вы используете другой
пакет-менеджер, процесс аналогичен: прописываете общесистемные плагины отдельно, а зависимые от языка файлы — внутри блока автозагрузки.


Такой подход помогает избежать избыточности и делает работу с редактором удобной и быстрой.
#528_C_Cpp_Vim_TLS

Набор плагинов для Vim для работы с С и С++.

Для эффективной разработки на C и C++ в редакторе Vim существует множество полезных плагинов, облегчающих работу программиста и повышающих продуктивность.


Основные категории плагинов.

Автодополнение и интеллектуальная навигация
эти плагины помогают быстрее писать код и лучше ориентироваться в проекте.

YouCompleteMeодин из лучших автодополнителей для Vim, поддерживает C/C++, JavaScript, Python и многие другие языки.
Работает быстро благодаря своему асинхронному движку.

Clangdинтегрирует LLVM/Clange-based сервер LSP для Vim, обеспечивая автодополнение, подсказки типов, переходы к определению и рефакторинг.

ALE (Asynchronous Lint Engine)проверяет синтаксис и стиль написания кода в режиме реального времени, поддерживая различные инструменты анализа кода (например, clang-tidy, cppcheck).

Vim-lsp
простое подключение к Language Server Protocol (LSP), которое позволяет получать автозавершение, проверку ошибок, подсказки и многое другое непосредственно из компилятора или инструментов статического анализа (таких как clangd).

Tagbar
показывает структуру текущего файла, отображая символы функций, классов, переменных и макросов, что помогает легко перемещаться между ними.

Vimsynstaxулучшенная подсветка синтаксиса для C и C++, включающая поддержку комментариев, строковых литералов, регулярных выражений и различных конструкций препроцессора.


Работа с файлами проекта и проектами.
Эти плагины позволяют управлять проектом и легко переключаться между различными частями вашей программы.

NERDTreeграфический файловый менеджер внутри редактора, позволяющий удобно просматривать дерево файлов и каталогов вашего проекта.

FZF.vimфильтрует файлы, строки и команды, позволяя мгновенно находить нужные элементы с помощью быстрого фильтра.

Vinegarулучшенный интерфейс для перемещения по директориям и работе с деревьями файлов прямо из Vim.

Projectile-vim
помогает управлять большими проектами — быстрое открытие файлов, быстрый доступ к задачам сборки, тестирования и деплоймента.

LeaderFбыстрый доступ к проектным элементам (файлы, буферы, команды) с использованием удобных горячих клавиш.


Отладка и тестирование.
Эти плагины упрощают процесс отладки и тестирования приложений.

Gdb.vimинтеграция с GNU Debugger (GDB).
Позволяет запускать дебаггер прямо из Vim, устанавливать точки останова, смотреть стек вызовов и значения переменных.

Cquery (или его замена bear-cmake) + LanguageClient-nvimиспользуется совместно с LSP-сервером cquery (замещён bear-cmake), позволяющим полноценно интегрироваться с IDE-подобными функциями вроде автозаполнения, определения символов и навигации по проекту.

Dbgp.vimдля удалённой отладки PHP-кода (если используете PHP вместе с C/C++).

Сpptest
поддерживает интеграцию тестов для библиотек Google Test, Boost.Test и Catch.

.
🔥1
Навигация и поддержка стандартов программирования.
Помощь в написании качественного и читаемого кода.

Surround.vim
быстро добавляет окружение вокруг текста (скобки, кавычки, фигурные скобки и т.п.).

IndentLine
отображает вертикальные линии отступов, помогающие визуально отслеживать уровни вложенности блоков кода.

Alignвыравнивает части исходного кода (например, выравнивает присвоенные значения, аргументы функций и комментарии).

Tabular
инструмент для автоматического выравнивания определённых элементов кода, типа объявлений переменных или аргументов функций.

Commentaryудобное комментирование выделенного блока кода одной командой (gcc), хорошо работает с разными языками программирования.

Ctagsгенерирует метаданные о проекте (функциях, классах, методах и т.д.) для быстрой навигации по ним.
Рекомендуется использовать в связке с Tagbar.


Форматирование и автоматическое исправление.
Улучшите качество своего кода автоматически!

ALE
проверяет код на наличие ошибок и предлагает улучшения стиля (поддерживает много утилит, включая clang-format, cppcheck и др.)

Prettier.vim
eсли важно соблюдать стандарты оформления, этот плагин интегрируется с Prettier для автоматического форматирования кода согласно заданным правилам.

autopep8, black, isortхотя эти плагины ориентированы больше на Python-код, но полезны и для проектов смешанного характера (например, если у вас используется скриптинг на Python).

Сlang-format
официальная утилита форматирования от разработчиков LLVM, которая отлично справляется с форматом кода для C/C++.


Дополнительные полезные дополнения.

Lightline.vim
минималистичный статусбар с информацией о файле, позиции курсора, наличии изменений и многом другом.

Unite.vimмощный механизм поиска, навигации и фильтрации всего: файлов, буферов, команд, истории и т.д.

Minimap.vim
мини-карта, показывающая общее представление структуры открытого файла.

Fugitive.vimполноценная интеграция Git в Vim, позволяет выполнять практически любые операции с репозиториями Git прямо из редактора.


Комбинируя перечисленные плагины, можно настроить мощную среду разработки для
C и C++ в Vim, значительно упростив жизнь и ускорив рабочий процесс.
#529_ALG_GO_PPPO

Какой способ вставки элементов в начало или конец слайса (slice — срез) наиболее оптимален в Golang?
Поэлемнтное копирование —> вставка нового значения —> копирование последующих элементов?
Или использование операции append()?



При вставке нового элемента в начало или середину среза в Go наиболее эффективным решением является использование метода копирования вручную ("поэлементное копирование"), поскольку операция append() предназначена больше для добавления новых элементов в конец среза и работает неэффективно при вставке внутрь существующего среза.


Почему лучше избегать append()?
Операция append() создает новый внутренний массив, куда сначала копируются элементы старого среза, затем добавляется новый элемент.
Это полезно именно тогда, когда новые элементы добавляются в конец, потому что здесь автоматически увеличивается длина слайса.
Однако, если вы пытаетесь добавить элемент в произвольную позицию внутри среза (например, в начало), выполнение операции append() влечет два этапа:
создание копии всего текущего среза (так как сдвиг всех последующих элементов необходим).
— вставка нового элемента перед существующими элементами.

Это значительно менее эффективно, особенно для больших срезов, так как вся работа сводится к ненужному двойному копированию памяти.


Ручное копирование эффективнее:
Лучше самому реализовать ручной перенос значений с использованием функции copy() для части массива, находящейся слева от новой позиции, и для части справа от неё.

Рассмотрим пример вставки элемента в середину:
package main

import (
"fmt"
)

func insertElement(slice []int, pos int, value int) []int {
lenSlice := len(slice);
newSlice := make([]int, lenSlice + 1);
// Копируем левую половину
copy(newSlice[:pos], slice[:pos]);
// Добавляем новый элемент
newSlice[pos] = value;
// Копируем правую половину
copy(newSlice[pos+1:], slice[pos:lenSlice]);
return newSlice;
}

func main() {
slice := []int{1, 2, 3, 4, 5}
pos := 2
value := 100
result := insertElement(slice, pos, value)
fmt.Println(result) // Выведет: [1 2 100 3 4 5]
}


Здесь мы создаем новый срез длиной на единицу больше, чем исходный, затем копируем необходимые фрагменты с обеих сторон вокруг добавляемого элемента.

Такой подход
гораздо быстрее, так как:
— используется прямая копия элементов.
— нет повторного выделения памяти, кроме необходимого увеличения размера массива ровно на одну ячейку.


Для эффективной вставки элемента в середину или начало среза используйте ручное копирование элементов, используя стандартную функцию copy().

Операцию append() лучше оставить для добавления элементов в конце срезов, там её производительность оптимальна.
#530_GIT_GO_TLS

Последовательность действий для создания нового проекта на Go и его размещения на Github c учетом современныйх подходов и лучших практик разработки проектов.


Создание нового проекта на Go и размещение его на GitHub включает ряд шагов, начиная от инициализации среды разработки и заканчивая публикацией исходного кода и обеспечением качественной архитектуры проекта.


Рассмотрим этапы разработки Go-проектов:


1. Подготовка окружения и создание проекта

Установка Goубедитесь, что у вас установлена последняя стабильная версия Go:
go version


Установите последнюю версию Go, следуя инструкциям на официальном сайте:
https://golang.org/doc/install.


Создание рабочего пространстваcоздаем рабочую директорию проекта:
mkdir go-project && cd go-project


Инициализируем модуль Go:
go mod init example.com/go-project


Здесь example.com/go-project уникальный путь к проекту, соответствующий стандартному пути импорта пакетов в Go.


Структура директорийиспользуйте следующую структуру файлов и директорий для проекта:
go-project/
├── cmd/
│ └── main.go # точка входа приложения
├── internal/
│ ├── config/ # конфигурационные файлы
│ ├── handler/ # обработчики HTTP-запросов
│ ├── model/ # модели данных
│ ├── repository/ # слой взаимодействия с базой данных
│ └── service/ # бизнес-логика сервиса
├── pkg/ # внешние библиотеки и общие утилиты
├── vendor/ # зависимости проекта (опционально)
├── .gitignore # файл игнорирования ненужных файлов
├── Dockerfile # Docker-файл для контейнеризации
├── Makefile # автоматизация сборки и запуска
├── README.md # описание проекта
├── go.mod # файл модуля
└── go.sum # хеш-зависимости



2. Настройка и написание начального кода.

Добавление точки входа (main.go) — cоздаем точку входа в проекте в файле cmd/main.go:
package main

import (
"fmt"
)

func main() {
fmt.Println("Hello from Go!")
}



Написание простой логикиcоздаем пример простейшего API-контроллера в пакете internal/handler.

Файл internal/handler/api.go:
package handler

import (
"net/http"
"github.com/gorilla/mux"
)

// HealthCheckHandler проверяет работоспособность сервера
func HealthCheckHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status": "ok"}`))
}

// RegisterRoutes регистрирует маршруты
func RegisterRoutes(router *mux.Router) {
router.HandleFunc("/healthz", HealthCheckHandler).Methods("GET")
}


Запуск сервера в cmd/main.go:
package main

import (
"log"
"net/http"
"github.com/gorilla/mux"
"go-project/internal/handler"
)

func main() {
router := mux.NewRouter()
handler.RegisterRoutes(router)
log.Fatal(http.ListenAndServe(":8080", router))
}



3. Тестирование и покрытие тестов.

Напишите тесты для контроллеров и сервисов.
Пример теста в internal/handler/api_test.go:
package handler

import (
"bytes"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
)

func TestHealthCheckHandler(t *testing.T) {
req, err := http.NewRequest("GET", "/healthz", nil)
if err != nil {
t.Fatal(err)
}

recorder := httptest.NewRecorder()
HealthCheckHandler(recorder, req)

body, _ := ioutil.ReadAll(recorder.Body)
expectedBody := `{"status":"ok"}`

if recorder.Code != http.StatusOK || string(body) != expectedBody {
t.Errorf("Expected response code %d and body '%s', got %d and '%s'", http.StatusOK, expectedBody, recorder.Code, string(body))
}
}



4. Автоматизация процесса сборки и тестирования.

Создайте Makefile для автоматизации процесса компиляции, тестирования и упаковки проекта:
.PHONY: build test run clean

build:
go build -o bin/app .

run:
go run ./cmd/main.go

test:
go test -v ./...

clean:
rm -rf bin/*


Теперь можно собрать приложение и запустить тесты:
make build
make test
./bin/app


.
5. Публикация проекта на GitHub.

Инициализация git-репозиторияcоздайте локальный репозиторий Git:
git init



Игнорирование файловcоздайте .gitignore файл, добавив туда стандартные исключения:
echo ".DS_Store
bin/
vendor/" > .gitignore


Или используйте готовый шаблон .gitignore: https://github.com/github/gitignore/blob/master/Go.gitignore


Добавьте и закоммитте файлы:
git add .
git commit -m "Initial project setup"



Создайте удаленный репозиторий на GitHubзалогиньтесь на GitHub и создайте новый пустой репозиторий с названием, соответствующим проекту.

Затем свяжите ваш локальный репозиторий с удаленным:
git remote add origin <url вашего репозитория на GitHub>


Отправляем наш первый коммит на сервер:
git push -u origin master



Контейнеризация и развертывание.

Создаем Dockerfile для контейнера нашего приложения:
# Dockerfile
FROM golang:alpine AS builder

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .

RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/app .
EXPOSE 8080
CMD ["./app"]


Собираем образ и запускаем контейнер:
docker build -t my-go-app .
docker run -p 8080:8080 my-go-app



Вот основные шаги для создания проекта на Go и его публикации на GitHub:
— установка Go и настройка рабочего пространства;
— использование современной организации каталогов и модулей;

— применение хорошей практики написания кода и тестирования;
— организовали процесса сборки и тестирование через
Makefile
;
— размещение проекта на GitHub и настройка конвейера доставки через
Docker.


Следование данным рекомендациям позволит поддерживать качественный и легко поддерживаемый проект.
#531_GO_TLS

Стандартный путь импорта пакета в Goопределяется правилами именования импортируемых модулей, основанными на концепции уникальности доменного имени разработчика и четкого указания местоположения пакета.
Этот путь должен однозначно определять источник происхождения пакета и обеспечивать возможность его повторного использования другими разработчиками.

Правила формирования стандартного пути импорта в Go:
Доменное имя владельца. Обычно используется полное доменное имя разработчика или организации, которое гарантирует уникальность названия пакета.
Например, если пакет разработан компанией Google, название начинается с "google.com" или аналогичного зарегистрированного домена.
Таким образом, можно видеть, откуда пришел пакет, и избегать конфликтов имен.

Поддомены или префиксы, уточняющие принадлежность проекта.
Например, если разработчик владеет сайтом example.com, путь может выглядеть как example.com/myproject или даже глубже, с указанием подпроекта: example.com/myproject/submodule.

Отсутствие верхнего уровня папок, относящихся к операционной системе или среде разработки.
Традиционно пакеты Go хранятся вне каталога GOPATH, и их расположение фиксируется именно на уровне сайта разработчика.

Пример правильного пути импорта:
Неправильно:
/home/user/projects/go_project/src/my_package


Правильно:
example.com/go-project/pkg/my_package


Причина заключается в том, что путь импортирования должен отражать публично доступное сетевое расположение, а не физическое положение файла на компьютере.


Как работает механизм импорта?
Когда вы используете команду go get, система автоматически загружает указанный вами пакет с указанного домена (если этот домен доступен и публикует код, например, на GitHub или другом хостинге).
Использование стандартных путей помогает избежать коллизий и обеспечивает ясность при работе с внешними библиотеками.

Формат полного пути:
<DOMAIN>/<PROJECT_NAME>[/SUB_PROJECT][/PACKAGE_NAME]


Например:
Пакет находится здесь: https://github.com/google/go-prometheus
Импорт осуществляется следующим образом:
import "github.com/google/go-prometheus"


Этот путь позволяет другим пользователям точно понимать происхождение пакета и его уникальную идентификацию.


Таким образом, стандартный путь импортаспособ обеспечить однозначную связь между именем пакета и источником его происхождения, позволяя эффективно интегрировать чужие библиотеки и делиться своими собственными решениями.
#532_GO_CMPL_TP

Что представляют собой в Golang интерфейсы "под капотом"?

Интерфейсы в Goинструмент абстрагирования и композиции типов, позволяющий писать гибкий и расширяемый код.
Понимание реализации интерфейсов "под капотом" помогает лучше осознать, почему интерфейсы работают именно так и как оптимизировать использование ресурсов.


Структура Интерфейса.
Под капотом интерфейс представляет собой двухэлементный кортеж (tuple), состоящий из двух полей:
type iface struct {
tab *itab // Таблица методов
data unsafe.Pointer // Указатель на значение
}


Поле tab (*itab)это таблица методов конкретного типа, реализующего интерфейс.
Она хранит следующую информацию:
— тип значения, которое реализует интерфейс;
— методы,
доступные для этого типа;
— размер и выравнивание
значений типа.

Таким образом, *itab позволяет динамически определить тип объекта во время исполнения программы и вызывает соответствующие методы.

Поле data это указатель на фактическое значение, соответствующее типу, указанному в таблице методов.
Например, если объект типа MyType, удовлетворяющий интерфейсу, хранится в переменной интерфейса, поле data будет указывать непосредственно на экземпляр структуры MyType.


Как работает реализация?
При присваивании переменной-интерфейсу какого-либо значения, компилятор проверяет, соответствует ли этот тип требуемым методам интерфейса.
Если да, то создается пара (таблица методов, указатель на значение) и сохраняется в структуре интерфейса.

Например:
type MyInterface interface {
Method() string
}

type MyStruct struct{}

func (m MyStruct) Method() string { return "Hello from MyStruct" }

var myVar MyInterface = MyStruct{}


Здесь при присвоении myVar := MyStruct{}, Go создает структуру интерфейса вида {&itabForMyStruct, &myStructInstance}, где:
itabForMyStruct — таблица методов для типа MyStruct
myStructInstance —
сам экземпляр структуры.


Преимущества такого подхода.
Динамическая диспетчеризацияпозволяет вызывать разные реализации одного метода для разных объектов одинакового интерфейса.
func main() {
var mi MyInterface
mi = MyStruct{}
fmt.Println(mi.Method()) // Hello from MyStruct

type AnotherStruct struct {}
func (a AnotherStruct) Method() string { return "Hello from AnotherStruct" }

mi = AnotherStruct{}
fmt.Println(mi.Method()) // Hello from AnotherStruct
}


Инкапсуляция поведенияможно объявлять поведение через интерфейсы, оставляя реализацию конкретных структур скрытыми от внешних модулей.

Производительностьреализация интерфейсов минимально влияет на производительность благодаря статической проверке соответствия интерфейсам на этапе компиляции и использованию таблиц методов для быстрого вызова функций.


Недостатки и ограничения.
Хотя механизм интерфейсов удобен и эффективен, существуют некоторые особенности, которые важно учитывать разработчику:
Интерфейс занимает минимум два слова памяти даже при пустых структурах, поскольку включает таблицу методов и указатель на значение.
— Присваивание значению интерфейса требует копирование данных, что может влиять на производительность для больших структур.
— Для каждого нового типа, реализующего интерфейс, создается новая таблица методов, что увеличивает расход памяти.


Таким образом, понимание внутренней реализации интерфейсов в Go даёт возможность осознанно проектировать API и улучшать производительность приложений.
#533_RUST_TP

Как считать в Rust несколько чисел из стандартного потока которые могут быть введены как на одной строке через пробельные символы, так и на нескольких строках?


Чтобы считать несколько чисел из стандартного потока ввода в Rust, независимо от того, были ли они введены на одной строке через пробелы или распределены по нескольким строкам, можно воспользоваться следующей стратегией:

Продолжаем чтение потоков ввода до тех пор, пока не встретим конец файла (EOF).
Каждое введённое значение превращаем в число.
— Все считанные числа добавляем в общий контейнер
(например, вектор).

Пример:
use std::io::{self, BufRead};

fn main() {
let stdin = io::stdin();
let reader = stdin.lock();

let numbers: Vec<i32> = reader
.lines() // Получаем итератор строк
.flat_map(|line_result| {
let line = line_result.unwrap(); // получаем строку
line.split_whitespace() // разбиваем строку по пробелам
.map(|number_str| number_str.parse::<i32>()) // пытаемся превратить каждую часть в число
.filter_map(Result::ok) // фильтруем только успешно распарсившиеся числа
.collect::<Vec<_>>() // собираем в промежуточный вектор
})
.collect(); // собираем итоговые числа в основной вектор

println!("Прочитанные числа: {:?}", numbers);
}


Детали реализации:
Буферизованное чтение: используем объект BufReader для эффективного чтения строк из стандартного ввода.
Работа с линиями ввода: итератор .lines() возвращает итерируемые строки, поступающие из потока ввода.
Парсинг каждой строки: каждая строка делится на токены с помощью .split_whitespace(), что позволяет извлекать отдельные числа, даже если они находятся на разных строках.
Фильтрация и обработка ошибок: используя .filter_map(Result::ok), отбрасываем строки, которые невозможно перевести в число, предотвращая аварийное завершение программы.
Собираем числа в единый вектор: итоговые числа собираются в один вектор с помощью метода .collect().


Особенности:
Эта реализация игнорирует строки, содержащие недопустимые символы (не числа). Таким образом, если пользователи вводят буквы или специальные символы среди чисел, они будут пропущены.
Программа прекращает работу, когда достигает конца ввода (обычно при нажатии комбинации клавиш Ctrl+D в терминале Linux/MacOS или Ctrl+Z в Windows).


Тестирование:
1 2 3
4 5
6
7 8 9


Программа вернёт:
Прочитанные числа: [1, 2, 3, 4, 5, 6, 7, 8, 9]



Таким образом, можно обрабатывать как однострочные, так и многострочные входные данные.
#534_GO_LIB_TP

Какие контейнеры есть в библиотеке container в Golang?

Стандартная библиотека Go включает пакет container, предоставляющий набор различных коллекций и контейнеров для хранения данных.


Основные типы контейнеров, доступные в этом пакете.


Пакет container/listреализует двунаправленный связанный список:
Type: List
Описание: Поддерживает операции вставки, удаления и перемещения элементов. Элементы хранятся в упорядоченном списке узлов, каждый из которых содержит произвольное значение и ссылки на предыдущие и последующие узлы.
Методы: добавление, удаление, получение первого и последнего элемента, итерация вперед и назад по списку.

Пример использования:
l := list.New()
l.PushFront("First")
l.PushBack("Last")
for e := l.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value)
}



Пакет container/ringреализует кольцевой буфер (кольцо):
Type: Ring
Описание: Кольцевые списки полезны для реализации циклических алгоритмов обработки данных. Каждый элемент кольца ссылается на следующий элемент и замыкается на первый элемент.
Методы: обход элементов, перемещение указателя, операция вращения кольца.

Пример использования:
r := ring.New(3)
r.Value = "One"
r.Next().Value = "Two"
r.Next().Next().Value = "Three"
for p := r.Do(func(x interface{}) { fmt.Println(x) }); p > 0; p-- {}



Пакет container/heapпредоставляет интерфейс для построения бинарных куч (Binary Heaps):
Interface: Heap
Описание: Куча является структурой данных, позволяющей эффективно находить минимальный/максимальный элемент. Бинарная куча часто используется для приоритетных очередей.
Основные методы интерфейса Heap: Push(), Pop().

Чтобы использовать кучу, необходимо реализовать собственный тип контейнера, удовлетворяющий требованиям интерфейса Heap (реализация методов Len(), Less(), Swap(), Push(), Pop()):
type IntHeap []int

func (h IntHeap) Len() int {
return len(h)
}

func (h IntHeap) Less(i, j int) bool {
return h[i] < h[j]
}

func (h IntHeap) Swap(i, j int) {
h[i], h[j] = h[j], h[i]
}

func (h *IntHeap) Push(x interface{}) {
*h = append(*h, x.(int))
}

func (h *IntHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}

h := &IntHeap{}
heap.Init(h)
heap.Push(h, 3)
heap.Push(h, 1)
heap.Push(h, 2)
fmt.Println(heap.Pop(h)) // Output: 1

Итоги:
container/list — двунаправленные списки.
container/ringкольцевые списки.
container/heapинтерфейс для реализации бинарных куч.


Эти контейнеры обеспечивают дополнительные возможности организации данных и облегчают решение многих стандартных задач программирования.
#535_GO_LIB_TP

Какие библиотеки чаще всего используют для взаимодействия с PostgreSQL в проектах на Golang?


Для работы с базой данных PostgreSQL в Go-проектах наиболее часто используются следующие библиотеки:

database/sql + lib/pqстандартный пакет Go (database/sql) совместно с драйвером lib/pq, который реализует интерфейс подключения к PostgreSQL.
Этот вариант подходит для большинства случаев, поскольку обеспечивает простоту интеграции и совместимость с различными базами данных.
import (
"database/sql"
_ "github.com/lib/pq"
)



sqlxрасширяет возможности стандартного пакета database/sql.
Она добавляет дополнительные удобные методы для сканирования результатов запросов и управления транзакциями.
Подходит для проектов, которым нужен простой доступ к данным и минимальное количество сторонних зависимостей.
import (
"github.com/jmoiron/sqlx"
)



pgxвысокопроизводительная библиотека для работы с PostgreSQL, созданная специально для производительности и функциональности.
Позволяет эффективно управлять соединениями, поддерживает продвинутые SQL-функции и оптимизирует работу с большим объемом данных.
import (
"github.com/jackc/pgx/v5"
)



gormORM-библиотека для Go, поддерживающая различные базы данных, включая PostgreSQL.
gorm позволяет легко создавать модели, миграционные скрипты и автоматически синхронизироваться с таблицами. Удобна для больших приложений с частым изменением структуры данных.
import (
"gorm.io/gorm"
"gorm.io/driver/postgres"
)



Другие менее распространенные варианты:
entинструмент автоматического построения моделей и схем данных поверх PostgreSQL. Обеспечивает удобное управление структурами данных и связи между ними.
pop — легковесная ORM для работы с PostgreSQL, ориентированная на удобство и минимализм API.


Каждая из перечисленных библиотек имеет свои преимущества и области применения.
Выбор зависит от требований проекта: производительность, сложность схемы данных, простота реализации CRUD-операций и потребности в ORM-подходе.
#536_GO_LIB_TP

Пакет context в Golang, для чего он нужен и с чем его едят?

Пакет context в Goстандартный пакет, используемый для передачи значений между API уровнями и отмены операций во всей цепочке вызовов функций.
Контекст предназначен для упрощения обработки состояний запросов и отмены длительных операций.


Для чего нужен контекст?

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

Вот такие ситуации решает пакет context в Golang!


Контекст — специальный инструмент, позволяющий передавать инструкции ("указания") от одной части программы к другой.
Представьте контекст как помощника-посредника, который передает важные сигналы другим частям кода.


Какие задачи решаются с помощью пакета context?

Передача сообщенийпусть, у нас запущено много разных процессов одновременно, каждый делает свою работу долго-долго. И вдруг главный процесс понял, что больше не нужны никакие вычисления!
Пакет context помогает передать сообщение "стоп" всем остальным процессам, чтобы они прекратили свою работу.

Пример: пользователь запустил программу, которая скачивает файлы из интернета.
Пользователь нажал кнопку "отмена загрузки" → программа мгновенно прекращает загрузку, даже если там сотни файлов.

Установка ограничений по временииногда важно ограничить длительность какого-то процесса.
Представим игру, где игроку дается ровно одна минута на выполнение задания. Через минуту игра автоматически останавливается независимо от того, успел игрок завершить задание или нет.

Пример: программа пытается подключиться к серверу базы данных.
Если соединение не устанавливается за 5 секунд, оно автоматически прерывается.

Передача важной информациичасто бывает, что некоторые процессы требуют какой-то дополнительной информации для своей работы.
Вместо того чтобы передавать кучу аргументов, удобно создать общий контейнер с нужной информацией и передавать его вместе с заданием.

Пример: сервер обрабатывает запрос пользователя и хочет сохранить информацию о пользователе, чтобы каждая последующая обработка знала, кем вызван этот запрос.


Основные интерфейсы и структуры:

Функции из пакета context:
Background()начинающий контекст, от которого зависят остальные.
Это как основа, от которой начинают строиться новые контексты.

WithCancel()создает новый контекст, который можно отменить принудительно в любое время.
То есть можно сказать нашему помощнику прекратить всё немедленно, как только потребуется.
WithTimeout()используется для ограничения продолжительности задачи по времени.
Удобно, когда хочешь убедиться, что задача не продлится дольше определенного периода.
WithDeadline()похож на предыдущий, но устанавливает точный срок окончания задачи, а не интервал ожидания.
WithValue()позволяет прикреплять дополнительные данные к контексту.
Очень удобно, если хочешь хранить какую-нибудь важную информацию вроде имени пользователя или сессии, которую потом можно достать из любого места программы.



Тип Contextпредставляет собой абстрактный тип контекста, поддерживающий два ключевых свойства:
Отменяемость операции.
Передачу произвольных данных («метаданных») внутри приложения.

Контекст создается функциями типа
WithCancel()
WithTimeout()
WithDeadline().


Каждый новый контекст наследует поведение родительского контекста.

Пример базового использования:
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, time.Second*5)
defer cancel()
// Выполнение длительной операции
result, err := longOperation(ctx)
if ctx.Err() != nil {
fmt.Println("Операция отменена:", ctx.Err())
}

.
Функция Background()возвращает пустой, незавершенный контекст, подходящий для корневых контекстов запросов.
Его нельзя отменить, и он не имеет дедлайна.
ctx := context.Background()

Function WithValue(parent Context, key interface{}, val interface{}) Context:


Создает производный
контекст с дополнительным значением.
Это полезно для хранения уникальных данных о конкретном запросе, таких как токены авторизации или идентификаторы пользователей.
type userKey struct{}

ctx = context.WithValue(ctx, userKey{}, "alice")
value := ctx.Value(userKey{})
fmt.Println(value) // Выведет alice



Функции отмены:


WithCancel(parent Context)cоздает дочерний контекст, который можно отменить отдельно от родителя. После вызова функции cancel(), связанный контекст получает ошибку отмены.

WithTimeout(parent Context, timeout time.Duration)устанавливает таймаут для завершения всех действий над данным контекстом.

WithDeadline(parent Context, deadline time.Time)устанавливает крайнюю точку времени, до которой должна завершаться операция.


Примеры создания временных контекстов:
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()

ctx, cancel = context.WithDeadline(context.Background(), time.Now().Add(time.Hour))
defer cancel()


Метод Err()возвращает ошибку, соответствующую состоянию контекста.

Возможные состояния:
Нет ошибок (если ничего не произошло),
Ошибка отмены,
Таймаут превышен.

select {
case <-ctx.Done():
fmt.Println("Отмена или таймаут:", ctx.Err())
default:
fmt.Println("Ожидание...")
}



Как правильно использовать?
— Всегда проверяйте состояние контекста перед началом длительной операции.
— Используйте дефер и отмену для своевременного освобождения ресурсов.
— Не передавайте один и тот же контекст нескольким параллельным операциям, создавая отдельный контекст для каждой.


Пакет context нужен, чтобы сообщать программам о важных событиях, ограничивать продолжительность выполнения задач и передавать дополнительную полезную информацию.
Пакет context позволяет эффективно управлять состоянием приложений, обеспечивая отмену асинхронных операций и передачу необходимых метаданных.
Этот механизм значительно облегчает написание отказоустойчивых сервисов и обработку сложных потоков выполнения.
#537_RUST_LIB_SDS

В Rust стандартная реализация хэш-таблицы называется HashMap, и она находится в модуле std::collections::HashMap.
HashMapассоциативный контейнер, который хранит пары «ключ — значение», где ключи уникальны и хэшируются для быстрого поиска.

Прежде чем использовать HashMap, его нужно импортировать:
 use std::collections::HashMap;


Создание пустой HashMap:
 let mut map: HashMap<&str, i32> = HashMap::new();


Инициализация с данными:
 use std::collections::HashMap;

let mut map = HashMap::from([
("один", 1),
("два", 2),
("три", 3),
]);


Для использования HashMap::from нужна версия Rust ≥ 1.56 (или включена соответствующая фича в более ранних версиях).

Или через вектор:
 let map: HashMap<_, _> = vec![("a", 1), ("b", 2), ("c", 3)].into_iter().collect();



Основные методы.

Вставка (insert):
 map.insert("четыре", 4);


Если ключ уже существует — старое значение заменяется, и возвращается предыдущее значение (опционально):
 let old_value = map.insert("один", 10); // old_value = Some(1)


Получение значения (get):
match map.get("два") {
Some(&value) => println!("Значение: {}", value),
None => println!("Ключ не найден"),
}


get возвращает Option<&V>, так как ключ может отсутствовать.

Удаление (remove):
let removed_value = map.remove("три"); // Option<i32>



Проверка наличия ключа (contains_key):
if map.contains_key("один") {
println!("Ключ 'один' существует");
}



Обновление только если ключ отсутствует (entry API):

use std::collections::hash_map::Entry;

match map.entry("пять") {
Entry::Occupied(_) => println!("Ключ уже существует"),
Entry::Vacant(e) => {
e.insert(5);
}
}



Или короче:
map.entry("пять").or_insert(5);



Если нужно вычислить значение лениво:
map.entry("шесть").or_insert_with(|| 2 * 3);




Другие полезные методы

len()возвращает количество пар «ключ-значение»;

is_empty()проверяет, пуст ли HashMap;

clear()удаляет все элементы;

iter()возвращает итератор по парам (&K, &V);

iter_mut() итератор с возможностью изменения значений;

keys() итератор по ключам;

values() итератор по значениям;

values_mut()итератор по изменяемым значениям;

Пример итерации:
for (key, value) in &map {
println!("{}: {}", key, value);
}




Ограничения на типы.

Ключи в HashMap должны реализовывать трейты:
Hashдля вычисления хэш-кода;
Eq — для сравнения на равенство.

Примеры допустимых типов ключей:
String, &str, i32, (i32, i32), пользовательские структуры (если реализованы Hash + Eq). 


Пример пользовательского ключа:
#[derive(Hash, Eq, PartialEq, Debug)]
struct Person {
id: u32,
name: String,
}

let mut people = HashMap::new();
people.insert(Person { id: 1, name: "Алиса".to_string() }, "инженер");


.
Владение и заимствование.

— при вставке String — владение передаётся в HashMap.
— при вставке &str — нужно, чтобы строка жила дольше HashMap.

Пример с владением:
let mut map = HashMap::new();
map.insert(String::from("ключ"), 42); // String передаётся по значению



Пример: подсчёт частоты слов:
use std::collections::HashMap;

let text = "rust is great and rust is fast";
let mut word_count = HashMap::new();

for word in text.split_whitespace() {
*word_count.entry(word.to_lowercase()).or_insert(0) += 1;
}

for (word, count) in &word_count {
println!("{}: {}", word, count);
}


Вывод:
rust: 2
is: 2
great: 1
and: 1
fast: 1



Производительность.
— Вставка, поиск, удаление — в среднем O(1).
Худший случай — O(n) при коллизиях, но маловероятен при хорошей хэш-функции.
По умолчанию используется хэш-функция SipHash, безопасная от DoS-атак.


Можно заменить хэшер (например, на ahash или fxhash) для повышения скорости:
 # Cargo.toml
[dependencies]
ahash = "0.8"


use ahash::AHasher;
use std::hash::BuildHasherDefault;

type FastMap<K, V> = HashMap<K, V, BuildHasherDefault<AHasher>>;

let mut map: FastMap<&str, i32> = HashMap::default();



Резюме:
HashMap основной способ хранения ассоциативных данных в Rust.
Требует Hash + Eq от ключей.
Безопасен благодаря системе владения и проверкам на этапе компиляции.
Богатый API: entry, итераторы, вставка/поиск/удаление и т.д.
Гибок: можно кастомизировать хэш-функцию.


Замечания:
Если нужно упорядоченное хранение — смотри в сторону BTreeMap.
Если нужен мульти-ключ или дублирующиеся ключи — hashbrown или другие сторонние решения.
🤡1
#538_Cpp_LIB_SDS_STL

Что делает std::cin.tie(NULL); в C++?

Операция std::cin.tie(NULL) — отменяет привязку (tie) потока ввода (std::cin) к потоку вывода (std::cout).


Суть привязки потоков (tie).
Потоки ввода и вывода связаны между собой по умолчанию в C++.
Это означает, что всякий раз, когда производится операция вывода (например, std::cout << something;), система синхронизирует потоки ввода и вывода, гарантируя, что ввод и вывод происходят синхронно.
То есть перед выполнением следующего шага чтения (std::cin) операционная система гарантирует, что предыдущий вывод (std::cout) завершён.

Это полезно в интерактивных приложениях, где нужно обеспечить согласованность ввода и вывода. Однако это замедляет программу, особенно если интенсивное взаимодействие с пользователями не требуется.


Зачем разрывать связь (tie(NULL))?

Повышение производительности синхронизация между потоками увеличивает накладные расходы.
Если приложение не нуждается в гарантии синхронизации (например, серверные приложения или программы с большими вычислительными нагрузками), разрыв связи между потоками может заметно ускорить работу программы.

Снижение задержкибез привязки вывод на экран не будет ждать завершения операции ввода, что уменьшает задержку, особенно в случаях интенсивного ввода-вывода.


Пример использования:
Допустим, есть следующая программа:
#include <iostream>

int main() {
std::cin.tie(NULL); // Отменяем привязку cin к cout

std::cout << "Enter your name: ";
std::string name;
std::cin >> name;

std::cout << "Hello, " << name << "!\n";
return 0;
}


До вызова std::cin.tie(NULL) вывод на экран происходил синхронно с вводом, то есть строка приглашения выводилась только после завершения предыдущего ввода.
После разрыва связи вывод немедленно появляется на экране, не дожидаясь завершения последующих операций ввода.


Важно помнить:
Разрыв связи между потоками может повлиять на визуальное представление приложений, работающих в режиме реального времени.
Будьте осторожны при применении этого трюка в ситуациях, где необходим аккуратный контроль взаимодействия с пользователем.
1🤡1
#539_Cpp_PY_LIB_SDS_STL_IdPt

Как в С++, организовать конструкцию подобную switch в Go или match в Rust и Python, которая позволит в качестве меток case использовать не только целочисленные и символьные значения?

Интересный не очень очевидный прием, в нем нет ничего очень умного или хитрого, но таким образом можно заменить конструкцию switch в С++ и при этом мы сможем оперировать не только целочисленными типами, но и строками выражениями и другими объектами (подобная же конструкция с использованием словаря вместо std::map в С++, способна заменить конструкцию math (конструкции switch или подобной ей в Python не было вплоть до версии 3.10)).

Давайте создадим простейший калькулятор:
#include <iostream>
#include <map>
#include <cmath>

int main() {
auto opd1 = 0., opd2 = 0.;
char operation = '\0';
std::cin >> opd1;
std::cin.ignore();
std::cin >> operation >> opd2;

if ('/' == operation && 0 == opd2) {
std::cerr << "Error. Division by zero.\n";
return 0;
}

std::map<char, int> calculate {
{'+', opd1 + opd2},
{'-', opd1 - opd2},
{'*', opd1 * opd2},
{'/', opd1 / opd2},
{'^', pow(opd1, opd2)},
{'%', static_cast<int>(opd1) % static_cast<int>(opd2)}
};

std::cout << calculate[operation];

return 0;
}


Таким образом просто добавляя новые пары ключ-значение в map — calculate, можно легко расширить функциональность, добавляя новые операции в наш калькулятор и получать результат вычислений просто обращаясь по ключу.


Все довольно просто, но, по моему мнению, не совсем очевидно, что в качестве значений std::map в С++ или словарей в Python, мы можем использовать выражения.
👀2🤡1