Многострочные строки в Go: 5 фатальных ошибок и как их избежать

Онлайн-тренажер Python для начинающих

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

Начать курс

Многострочные строки в Go: ловушки, которые подстерегают даже опытных разработчиков

Представьте ситуацию: вы пишете на Go SQL-запрос, JSON-шаблон или просто длинное сообщение для лога. Вы интуитивно нажимаете Enter, чтобы перенести строку, и… компилятор выдает ошибку. Знакомо? Вопрос "How do you write multiline strings in Go?" на Stack Overflow собрал тысячи просмотров не просто так. Это одна из тех мелочей, которая способна поставить в тупик даже тех, кто уже освоил синтаксис языка.

В этой статье мы разберем топ-5 фатальных ошибок при работе с многострочными строками в Go и покажем, как их избежать. Вы узнаете, чем отличаются интерпретируемые и сырые литералы, почему лишний пробел может сломать вам день и как правильно форматировать код. Поехали!

Ошибка №1: Использование двойных кавычек для многострочного текста

Самая распространенная ошибка новичков — попытка написать многострочную строку внутри двойных кавычек ("). В Go двойные кавычки создают интерпретируемый строковый литерал. Это значит, что символ переноса строки внутри них запрещен.

// Этот код НЕ скомпилируется
package main

import "fmt"

func main() {
    // Ошибка: unexpected newline in string literal
    message := "Привет,
    мир!"
    fmt.Println(message)
}

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

Ошибка №2: Игнорирование сырых строковых литералов (raw string literals)

Решение проблемы многострочности — сырые строковые литералы. Они создаются с помощью обратных кавычек (`). Внутри них все символы интерпретируются буквально, включая переносы строк, табуляцию и даже обратную косую черту.

// Правильный способ: используем обратные кавычки
package main

import "fmt"

func main() {
    message := `Привет,
мир!`
    fmt.Println(message)
}
// Вывод:
// Привет,
// мир!

Это идеальный вариант для SQL-запросов, JSON, HTML-шаблонов и любого другого текста, где важна структура. Однако здесь скрывается следующая ошибка.

Ошибка №3: Лишние пробелы и отступы внутри сырых строк

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

// Пример с "лишними" пробелами
package main

import "fmt"

func main() {
    query := `
        SELECT id, name
        FROM users
        WHERE active = true
    `
    fmt.Println(query)
}
// Вывод (обратите внимание на ведущие пробелы):
//
//         SELECT id, name
//         FROM users
//         WHERE active = true
//     

Первая строка вывода пустая (потому что перенос после открывающей кавычки), а каждая следующая строка начинается с 8 пробелов. Это может сломать логику, если строка используется, например, в os.Exec или при сравнении.

Как избежать? Есть два пути. Первый — писать строку без отступов, вплотную к краю:

query := `SELECT id, name
FROM users
WHERE active = true`

Второй — использовать конкатенацию или strings.TrimSpace. Но проще всего — приучить себя не делать отступов внутри сырых строк, если они критичны. Для SQL-запросов это часто не проблема, но для логов или сообщений пользователю — может стать фатальной.

Ошибка №4: Забываем про экранирование в сырых строках

Сырые строки не поддерживают escape-последовательности. Это значит, что \n внутри обратных кавычек — это просто два символа: обратная косая черта и буква 'n', а не перенос строки.

package main

import "fmt"

func main() {
    // Ожидаем перенос строки, но получаем текст с \
    message := `Строка1\nСтрока2`
    fmt.Println(message)
}
// Вывод: Строка1\nСтрока2

Это частый источник багов, когда разработчик привык к другим языкам. Если вам нужно смешать многострочность и escape-последовательности (например, вставить символ табуляции \t), придется использовать конкатенацию или fmt.Sprintf.

Ошибка №5: Неправильная конкатенация и оператор "+"

Иногда сырые строки не подходят — например, когда вам нужно динамически собрать строку из переменных. Тогда в ход идет конкатенация. Но и тут есть подводные камни.

Многие пишут так:

// Синтаксически верно, но некрасиво
s := "Это очень длинная строка, " +
    "которая не помещается в одну линию " +
    "и мы ее переносим."

Это работает, но есть нюанс: Go автоматически не добавляет пробел или перенос строки между частями. Вам нужно делать это вручную. Если вы забудете пробел в конце первой части, слова склеятся.

Более элегантный способ — использовать strings.Join или fmt.Sprintf:

package main

import (
    "fmt"
    "strings"
)

func main() {
    // Вариант 1: strings.Join
    lines := []string{
        "Это первая строка",
        "Это вторая строка",
        "Это третья строка",
    }
    result := strings.Join(lines, "\n")
    fmt.Println(result)

    // Вариант 2: fmt.Sprintf (удобно с переменными)
    name := "Алексей"
    age := 30
    bio := fmt.Sprintf(`Имя: %s
Возраст: %d
Город: Москва`, name, age)
    fmt.Println(bio)
}

strings.Join позволяет задать разделитель — пробел, запятую или перенос строки. А fmt.Sprintf отлично работает с шаблонами, особенно если вы используете сырые строки для форматирования.

Бонус: Как писать чистый код с многострочными строками

Чтобы ваши многострочные строки были читаемыми и предсказуемыми, следуйте этим правилам:

  • Для статического текста (SQL, JSON, конфиги) — используйте сырые литералы (`...`). Они исключают путаницу с escape-символами.
  • Для динамического текста — используйте fmt.Sprintf или strings.Builder (если строк много).
  • Не бойтесь выносить длинные строки в отдельные переменные или даже в отдельные файлы (например, для SQL-запросов).
  • Используйте

Блоги

Рекомендаций по книгам