function / () => {} → func (first-class, но без this)
class → struct + методы с receiver
interface в TS → неявный интерфейс в Go (duck typing)
extends → embedding (композиция)
try/catch → возврат (result, error)
mkdir go-funcs && cd go-funcs
go mod init go-funcs
package main
import (
"errors"
"fmt"
"math"
"strings"
)
// ╔══════════════════════════════════════════════════════════╗
// ║ 1. ФУНКЦИИ — БАЗОВЫЕ ВОЗМОЖНОСТИ ║
// ╚══════════════════════════════════════════════════════════╝
// Простая функция: параметры (a, b int) — сокращение для (a int, b int)
func add(a, b int) int {
return a + b
}
// Множественный возврат — идиоматичный паттерн Go.
// В JS: const [result, error] = await divide(...)
// В Go: result, err := divide(...)
func divide(a, b float64) (float64, error) {
if b == 0 {
// errors.New создаёт простую ошибку. fmt.Errorf — с форматированием.
return 0, fmt.Errorf("деление на ноль: %.2f / %.2f", a, b)
}
return a / b, nil // nil — отсутствие ошибки
}
// Именованные возвращаемые значения.
// Имена x,y в сигнатуре — это переменные, доступные внутри функции.
// "naked return" — возвращает текущие значения x и y.
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // "голый" возврат (использовать осторожно, ухудшает читаемость)
}
// Вариативная функция: parts ...string — аналог ...rest в JS
func concat(separator string, parts ...string) string {
return strings.Join(parts, separator)
}
// Функция как first-class citizen: принимает другую функцию
func applyOperation(a, b int, op func(int, int) int) int {
return op(a, b)
}
// Замыкание (closure): функция захватывает переменную из внешней области
func counter() func() int {
count := 0
return func() int {
count++ // мутирует захваченную переменную
return count
}
}
// ╔══════════════════════════════════════════════════════════╗
// ║ 2. СТРУКТУРЫ И МЕТОДЫ ║
// ╚══════════════════════════════════════════════════════════╝
// Rectangle — структура (аналог класса без методов)
type Rectangle struct {
Width float64
Height float64
}
// Value receiver: (r Rectangle) — метод получает КОПИЮ структуры.
// Не может изменить оригинал. Используй для иммутабельных операций.
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
func (r Rectangle) Description() string {
return fmt.Sprintf("Прямоугольник %.1f × %.1f", r.Width, r.Height)
}
// Circle — ещё одна структура
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
// Pointer receiver: (c *Circle) — метод получает УКАЗАТЕЛЬ.
// Может ИЗМЕНИТЬ оригинал. Используй для мутаций и больших структур.
func (c *Circle) Scale(factor float64) {
c.Radius *= factor // меняет оригинал через указатель
}
// Pointer receiver для проверки nil (метод можно вызвать на nil-указателе!)
func (c *Circle) IsValid() bool {
return c != nil && c.Radius > 0
}
// ╔══════════════════════════════════════════════════════════╗
// ║ 3. ИНТЕРФЕЙСЫ (неявная реализация) ║
// ╚══════════════════════════════════════════════════════════╝
// Shape — интерфейс. Любой тип с методами Area() и Perimeter()
// АВТОМАТИЧЕСКИ удовлетворяет этому интерфейсу.
// Не нужно писать "implements Shape" — duck typing на этапе компиляции.
type Shape interface {
Area() float64
Perimeter() float64
}
// Describable — ещё один интерфейс
type Describable interface {
Description() string
}
// PrintShapeInfo — функция, работающая с ЛЮБОЙ фигурой (полиморфизм)
func PrintShapeInfo(s Shape) {
fmt.Printf(" Площадь: %.2f | Периметр: %.2f\n", s.Area(), s.Perimeter())
// Если фигура ещё и Describable — выводим описание
if d, ok := s.(Describable); ok { // type assertion
fmt.Printf(" Описание: %s\n", d.Description())
}
}
// Пустой интерфейс interface{} (или any в Go 1.18+)
// Принимает ЛЮБОЙ тип. Аналог unknown в TS.
func printAny(v interface{}) {
fmt.Printf("Значение: %v (тип: %T)\n", v, v)
}
// ╔══════════════════════════════════════════════════════════╗
// ║ 4. ВСТРАИВАНИЕ (embedding) — композиция вместо наследования ║
// ╚══════════════════════════════════════════════════════════╝
// Animal — базовая структура
type Animal struct {
Name string
Age int
}
func (a Animal) Speak() string {
return fmt.Sprintf("%s издаёт звук", a.Name)
}
func (a Animal) Info() string {
return fmt.Sprintf("%s, %d лет", a.Name, a.Age)
}
// Dog встраивает Animal (композиция, НЕ наследование)
type Dog struct {
Animal // embedding: поля и методы Animal "продвигаются" в Dog
Breed string
}
// "Переопределение" метода Speak (shadowing)
func (d Dog) Speak() string {
return fmt.Sprintf("%s (породы %s) лает: Гав-гав!", d.Name, d.Breed)
}
// Cat тоже встраивает Animal
type Cat struct {
Animal
Color string
}
func (c Cat) Speak() string {
return fmt.Sprintf("%s (окрас %s) мяукает: Мяу!", c.Name, c.Color)
}
// Speaker — интерфейс для всех, кто умеет говорить
type Speaker interface {
Speak() string
}
func makeThemSpeak(speakers ...Speaker) {
for _, s := range speakers {
fmt.Println(" ", s.Speak())
}
}
// ╔══════════════════════════════════════════════════════════╗
// ║ MAIN ║
// ╚══════════════════════════════════════════════════════════╝
func main() {
fmt.Println("╔══════════════════════════════════════════╗")
fmt.Println("║ ФУНКЦИИ, МЕТОДЫ И ИНТЕРФЕЙСЫ ║")
fmt.Println("╚══════════════════════════════════════════╝")
// ==========================================
// 1. ВЫЗОВЫ ФУНКЦИЙ
// ==========================================
fmt.Println("\n── 1. ФУНКЦИИ ──")
fmt.Printf("add(3, 5) = %d\n", add(3, 5))
// Обработка ошибки: идиоматический паттерн
if result, err := divide(10, 3); err != nil {
fmt.Printf("Ошибка: %v\n", err)
} else {
fmt.Printf("10/3 = %.2f\n", result)
}
if _, err := divide(10, 0); err != nil {
fmt.Printf("Ошибка (ожидаемо): %v\n", err)
}
x, y := split(17)
fmt.Printf("split(17) = %d, %d\n", x, y)
fmt.Printf("concat: %s\n", concat(" → ", "Go", "Rust", "TypeScript"))
// Функция как аргумент (анонимная/лямбда)
multiply := func(a, b int) int { return a * b }
fmt.Printf("applyOperation(4,7, multiply) = %d\n",
applyOperation(4, 7, multiply))
// Замыкание
next := counter()
fmt.Println("counter:", next(), next(), next()) // 1 2 3
// ==========================================
// 2. СТРУКТУРЫ И МЕТОДЫ
// ==========================================
fmt.Println("\n── 2. СТРУКТУРЫ И МЕТОДЫ ──")
rect := Rectangle{Width: 5, Height: 3}
fmt.Printf("Rectangle: %.1f×%.1f | Area=%.1f | Perimeter=%.1f\n",
rect.Width, rect.Height, rect.Area(), rect.Perimeter())
circle := Circle{Radius: 2.5}
fmt.Printf("Circle: r=%.1f | Area=%.2f | Perimeter=%.2f\n",
circle.Radius, circle.Area(), circle.Perimeter())
// Pointer receiver: Scale меняет оригинал
fmt.Printf(" До Scale(2): r=%.1f\n", circle.Radius)
circle.Scale(2) // Go автоматически берёт (&circle).Scale(2)
fmt.Printf(" После Scale(2): r=%.1f\n", circle.Radius)
// Проверка nil-указателя
var nilCircle *Circle
fmt.Printf("nil circle valid? %v\n", nilCircle.IsValid())
// ==========================================
// 3. ИНТЕРФЕЙСЫ
// ==========================================
fmt.Println("\n── 3. ИНТЕРФЕЙСЫ ──")
// Слайс интерфейсов — полиморфизм
shapes := []Shape{
Rectangle{Width: 5, Height: 3},
Circle{Radius: 2.5},
Rectangle{Width: 10, Height: 2},
}
for i, s := range shapes {
fmt.Printf("Фигура %d:\n", i+1)
PrintShapeInfo(s)
}
// Пустой интерфейс
fmt.Println("\nПустой интерфейс (any):")
printAny(42)
printAny("hello")
printAny(Rectangle{Width: 3, Height: 4})
// Type assertion — извлечение конкретного типа из интерфейса
var s Shape = Circle{Radius: 5}
if c, ok := s.(Circle); ok {
fmt.Printf("Извлекли Circle: r=%.1f\n", c.Radius)
}
// Type switch
fmt.Println("\nType switch:")
describeShape := func(s Shape) {
switch v := s.(type) {
case Rectangle:
fmt.Printf(" Прямоугольник %.1f×%.1f\n", v.Width, v.Height)
case Circle:
fmt.Printf(" Круг r=%.1f\n", v.Radius)
default:
fmt.Printf(" Неизвестная фигура\n")
}
}
describeShape(Rectangle{Width: 5, Height: 3})
describeShape(Circle{Radius: 2})
// ==========================================
// 4. ВСТРАИВАНИЕ (композиция)
// ==========================================
fmt.Println("\n── 4. ВСТРАИВАНИЕ (embedding) ──")
dog := Dog{
Animal: Animal{Name: "Бобик", Age: 3},
Breed: "Лабрадор",
}
cat := Cat{
Animal: Animal{Name: "Мурка", Age: 2},
Color: "Чёрный",
}
// Прямой доступ к полям встроенной структуры
fmt.Printf("Собака: %s | %s\n", dog.Name, dog.Info())
fmt.Printf("Кошка: %s | %s\n", cat.Name, cat.Info())
// Полиморфизм через интерфейс Speaker
fmt.Println("\nВсе говорят:")
makeThemSpeak(dog, cat)
// ==========================================
// 5. СРАВНЕНИЕ VALUE vs POINTER RECEIVER
// ==========================================
fmt.Println("\n── 5. VALUE vs POINTER RECEIVER ──")
fmt.Println("Когда использовать pointer receiver (*T):")
fmt.Println(" ✓ Метод изменяет состояние структуры")
fmt.Println(" ✓ Структура большая (копирование дорого)")
fmt.Println(" ✓ Для согласованности, если хоть один метод pointer")
fmt.Println("Когда использовать value receiver (T):")
fmt.Println(" ✓ Структура маленькая и неизменяемая")
fmt.Println(" ✓ Тип — map, slice, chan (уже ссылочные)")
fmt.Println(" ✓ Нужна потокобезопасность через копирование")
}
# Запуск
go run main.go
# Сборка
go build -o funcs-demo main.go
./funcs-demo
| Критерий | Value receiver (T) | Pointer receiver (*T) |
|---|---|---|
| Изменение оригинала | ❌ Нет (копия) | ✅ Да |
| Копирование | Копирует всю структуру | Копирует только адрес (8 байт) |
| Вызов на nil | Паника | Можно проверить if r == nil |
| Потокобезопасность | Безопаснее (копия) | Нужна синхронизация |
| Реализация интерфейса | T и *T | Только *T |
💡 Для Node.js разработчика: