Вход на сайт

Просмотр новости

Найдите то, что Вас интересует

Ошибка, которую никто никогда не совершал. Ведь так, да?

Дата публикации: 17-07-2026 12:07:04

Уже с первого курса университета — или даже раньше — вы могли догадаться, что писать arr[len(arr)] — плохая затея. И вряд ли так кто-то может ошибиться, правда? Мы тоже так думали, пока не проверили 1000 проектов. Читать далее

Основное содержимое страницы с новостью.

Уже с первого курса университета — или даже раньше — вы могли догадаться, что писать arr[len(arr)] — плохая затея. И вряд ли так кто-то может ошибиться, правда? Мы тоже так думали, пока не проверили 1000 проектов.

857f6d8ce218a2e710a7b7a25fbe1e4f.pngНа всякий случай объясню ошибку

Любой разработчик на Go знает, что встроенная функция len() принимает значение и возвращает длину в соответствии с типом значения.

Обратимся к документации и подробнее рассмотрим сигнатуру функции:

func len(v Type) int

Встроенная функция len возвращает длину v в соответствии с типом переданного значения:

  • Массив: количество элементов в v.

  • Указатель на массив: количество элементов в *v (даже если v равно nil).

  • Срез или карта: количество элементов в v; если v равно nil, len(v) равно нулю.

  • Строка: количество байт в v.

  • Канал: количество элементов в очереди (непрочитанных) в буфере канала; если v равно nil, len(v) равно нулю.

Рассмотрим простой пример:

func SimpleExample(arr []int) {
  _ = arr[len(arr)]
}

Индексы коллекций всегда начинаются с 0. Соответственно, для слайса длины n допустимые индексы находятся в диапазоне от 0 до n – 1. И len(arr) вернёт n. Поэтому обращение к arr[len(arr)] всегда приведёт к панике программы.

Что такое V8031 и как мы это нашли?

Небольшое предисловие: если кто-то не знает, мы разрабатываем статический анализатор кода PVS-Studio для C, C++, C# и Java. Совсем недавно у нас закончилась программа раннего доступа к новым анализаторам для Go и JS/TS. Сейчас мы активно готовимся к их релизу, который будет в августе. Примерно в то же время стартует программа раннего доступа для PVS-Studio Atlas — командного решения для управления результатами анализа кода. Следите за новостями.

Возвращаемся к V8031. Это диагностика для Go анализатора. Когда мы её делали, то не думали, что она сможет дать такие хорошие результаты. Даже предполагали, что она может ничего не найти. Возникали мысли: “А вдруг это надуманная ошибка, которую отыщут на любом ревью или любым тестом?” Но по опыту знали, что есть десятки таких типов ошибок, которые вроде бы должны легко находиться, но их пропускают из-за лени, невнимательности или усталости.

Реализовать диагностику, естественно, было легко. С точки зрения реализации она сделана на чистом AST и по минимуму использует наши наработки сверх того, что предоставляют встроенные средства для анализа в Go. Кстати, если захотите, то можете сделать свои собственные диагностические правила. Начать можно с нашей ознакомительной статьи “Как сделать свой статический анализатор для Go?”.

После реализации наступает этап тестирования. В нашей команде он состоит из трёх частей:

  1. Unit-тестирование;

  2. Запуск диагностического правила на нашей выборке проектов с GitHub;

  3. Запуск диагностического правила на 1000 самых популярных Go проектах (отбираем по количеству звёзд).

Сейчас нас интересует именно третий этап. Как только мы получили результаты, то сильно удивились. Давайте посмотрим на них.

Результаты

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

Проект №1

bfe — современный балансировщик нагрузки, использующийся в Baidu.

type BasicLitList []*BasicLit
....
func (b BasicLitList) End() token.Pos {
  return b[len(b)].End()
}

Предупреждение PVS-Studio: V8031 Using the call of the ‘len’ function as index will cause off-by-one error. Consider using ‘len(b) - 1’ instead. ast.go 138

Проект №2

Ferret — это программируемый механизм извлечения данных и автоматизации для разработчиков.

func (vec *Vector) Push(value core.Value) *Vector {
  slice := vec.getCurrentSlice()
  slice[len(slice)] = value

  return vec
}

Предупреждение PVS-Studio: V8031 Using the call of the ‘len’ function as index will cause off-by-one error. Consider using ‘len(slice) - 1’ instead. vector.go 28

Проект №3

fq — инструмент, язык и декодеры для работы с двоичными и текстовыми форматами.

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

var blockFns = map[uint64]func(d *decode.D, dc *decodeContext){
  ....
  blockTypeInterfaceDescription: func(d *decode.D, dc *decodeContext) {
    typ := d.FieldU16("link_type", format.LinkTypeMap)
    d.FieldU16("reserved")
    d.FieldU32("snap_len")
    d.FieldArray("options", ....)

  dc.interfaceTypes[len(dc.interfaceTypes)] = int(typ)
  },
  ....
}

Предупреждение PVS-Studio: V8031 Using the call of the ‘len’ function as index will cause off-by-one error. Consider using ‘len(dc.interfaceTypes) - 1’ instead. pcapng.go 234

Проект №4

Incus — это современная система управления контейнерами и менеджер виртуальных машин.

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

func qemuRawCfgOverride(conf []cfg.Section, confOverride string) (....) {
  ....
  content := &sectionContent{
    comment: sec.Comment,
    entries: entries,
  }

  indexedSection[len(indexedSection)] = content
  orderedSections = append(orderedSections, section{
    name:    sec.Name,
    content: content,
  })
  ....
}

Предупреждение PVS-Studio: V8031 Using the call of the ‘len’ function as index will cause off-by-one error. Consider using ‘len(indexedSection) - 1’ instead. driver_qemu_config_override.go 50

А в этом проекте были выданы два предупреждения:

func (f *heartbeatFixture) node() (*state.State, *cluster.Gateway, string) {
  ....
  f.gateways[len(f.gateways)] = gateway
  f.states[gateway] = state
  f.servers[gateway] = server

  return state, gateway, address
}

V8031 Using the call of the ‘len’ function as index will cause off-by-one error. Consider using ‘len(f.gateways) - 1’ instead. heartbeat_test.go 253

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

Подводим итог

Вот таким интересным результатом мы хотели поделиться с вами. Вероятность мала, но, возможно, эта ошибка есть в вашем проекте. Не так давно мы рассказывали о ещё одной интересной ошибке в статье “^ != <<”. А чтобы код проекта был чистым, вы можете использовать статический анализатор PVS-Studio.

Удачи!

Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Artem Rovenskii. One mistake no one has ever made. Right?.

Схожие новости

#Наименование новостиТональностьИнформативностьДата публикации
1Строки кода должны помещаться на экране013.5801-12-2025
2Баги на диком западе: топ-10 ошибок в C и C++ проектах за 2025 год08.0630-12-2025
3Топ-10 ошибок, найденных в C# проектах за 2025 год015.7229-12-2025
4Через меня проходит 5 тысяч чертежей в год, и 99% ошибок в них одинаковые09.523-07-2026
5[Перевод] Большинство ошибок при System Design ускользает из виду011.0824-07-2026
610 самых интересных ошибок в Java проектах за 2025 год012.8926-12-2025
7Ваши тесты упали по причине JavaScript011.7717-11-2025
8Ошибка в коде, на которую приходится не обращать внимание-16.7208-06-2026
9Мне надоело писать один и тот же код. Поэтому я сделал Featuregen3609-07-2026
10Ошибки и подозрительные места в исходниках .NET 10012.6604-06-2026

Классификация: Наука. Схожих патентов: 0. Схожих новостей: 10. Тональность: -1. Информативность: 11.32. Источник: habr.com.