Почему Go — это не остров, а мост
Go — замечательный язык. Он быстрый, компилируемый, с отличной стандартной библиотекой. Но давайте честно: в реальном мире мы редко пишем всё с нуля на одном языке. У вас есть старая, проверенная временем библиотека на C, которую жалко переписывать. Или крутой скрипт на Python для машинного обучения. Или вы хотите запустить логику на Go прямо в браузере.
И тут встаёт вопрос: как интегрировать Go с другими языками? Не бойтесь, Go специально проектировался как хороший «сосед». В этой статье на PythonLib мы разберём три главных способа подружить вашу Go-программу с чужим кодом. Вы узнаете, как работает cgo, как дёргать Python-функции через системные вызовы и даже как компилировать Go в WebAssembly.
Интеграция Go с C через cgo: классика жанра
Самый прямой и мощный путь — это cgo. Это не библиотека, а встроенный механизм Go, который позволяет вашему коду напрямую вызывать C-функции. Как это работает? Вы просто пишете импорт "C" и перед ним — комментарий с кодом на C. Go-компилятор всё склеит.
Пишем первую программу с cgo
Давайте напишем простой пример: вызовем стандартную printf из C. Создайте файл main.go:
package main
/*
#include <stdio.h>
#include <stdlib.h>
void hello_cgo(const char* name) {
printf("Привет из C, %s!\n", name);
}
*/
import "C"
import "unsafe"
func main() {
name := C.CString("Gopher")
defer C.free(unsafe.Pointer(name))
C.hello_cgo(name)
}
Запускаем: go run main.go. Если у вас установлен GCC (или MinGW на Windows), всё скомпилируется. Вы увидите «Привет из C, Gopher!». Магия!
Важно: C.CString выделяет память в куче C, поэтому мы обязаны её освободить через C.free. Иначе — утечка. Cgo требует от вас дисциплины.
Передача сложных типов: структуры
Часто нужно работать не со строками, а со структурами. Допустим, у нас есть C-библиотека для работы с матрицами. Мы хотим передать в неё Go-структуру. Сделаем это:
package main
/*
typedef struct {
int x;
int y;
int result;
} Point;
Point add_points(Point a, Point b) {
Point r;
r.x = a.x + b.x;
r.y = a.y + b.y;
r.result = r.x + r.y;
return r;
}
*/
import "C"
import "fmt"
type GoPoint struct {
X, Y, Result int
}
func main() {
a := C.Point{x: 10, y: 20}
b := C.Point{x: 30, y: 40}
r := C.add_points(a, b)
goPoint := GoPoint{
X: int(r.x),
Y: int(r.y),
Result: int(r.result),
}
fmt.Printf("Go получил: Point{X:%d, Y:%d, Result:%d}\n", goPoint.X, goPoint.Y, goPoint.Result)
}
Обратите внимание: мы явно преобразуем C-типы в Go-типы. Cgo не умеет автоматически маппить структуры, поэтому вы всегда отвечаете за конвертацию. Это один из главных компромиссов интеграции Go с языком C.
Когда cgo не нужен
Не используйте cgo, если:
- Вам нужно просто позвать команду в терминале — используйте
os/exec. - Вы пишете кроссплатформенный код — cgo усложняет сборку.
- Производительность не критична — cgo добавляет накладные расходы на переключение контекста.
Интеграция Go с Python: скриптуем с умом
Python и Go — отличная пара. Go — за скорость и параллелизм, Python — за гибкость и экосистему (NumPy, TensorFlow). Как их подружить? У нас есть два подхода: «тяжёлый» (через cgo и Python C API) и «лёгкий» (через подпроцессы).
Способ 1: Вызов Python через os/exec (простой и надёжный)
Самый безопасный для образования способ — запустить Python-скрипт как отдельный процесс. Go-программа передаёт данные через stdin/stdout или JSON-файл.
package main
import (
"encoding/json"
"fmt"
"os/exec"
"strings"
)
type Input struct {
Values []float64 `json:"values"`
}
type Output struct {
Mean float64 `json:"mean"`
Std float64 `json:"std"`
}
func main() {
// Данные для Python
input := Input{Values: []float64{2.5, 3.0, 4.1, 5.6}}
inputJSON, _ := json.Marshal(input)
// Запускаем Python-скрипт
cmd := exec.Command("python3", "compute_stats.py")
cmd.Stdin = strings.NewReader(string(inputJSON))
outputBytes, err := cmd.Output()
if err != nil {
panic(err)
}
var result Output
json.Unmarshal(outputBytes, &result)
fmt.Printf("Среднее: %.2f, Стандартное отклонение: %.2f\n", result.Mean, result.Std)
}
А вот сам скрипт compute_stats.py:
import sys, json, numpy as np
data = json.load(sys.stdin)
arr = np.array(data['values'])
result = {
'mean': float(np.mean(arr)),
'std': float(np.std(arr))
}
print(json.dumps(result))
Плюсы: простота, не нужно компилировать Python в Go. Минусы: оверхед на запуск процесса. Для вызова одного скрипта — идеально.
Способ 2: Встраивание Python через cgo (для профи)
Если вам нужно вызывать Python-функции сотни раз в секунду, запуск процесса не годится. Тогда идём в интеграцию Go с Python через Python.h. Пример (только для Linux/macOS):
package main
/*
#cgo LDFLAGS: -lpython3.10
#include <Python.h>
*/
import "C"
import "fmt"
func main() {
C.Py_Initialize()
defer C.Py_Finalize()
// Выполняем Python-код
code := C.CString("result = 42 + 10")
defer C.free(unsafe.Pointer(code))
C.PyRun_SimpleString(code)
// Получаем результат
mainModule := C.PyImport_AddModule(C.CString("__main__"))
dict := C.PyModule_GetDict(mainModule)
resultObj := C.PyDict_GetItemString(dict, C.CString("result"))
if resultObj != nil {
val := int(C.PyLong_AsLong(resultObj))
fmt.Printf("Python сказал: %d\n", val)
}
}
Этот код требует, чтобы у вас был установлен Python dev-пакет (python