Как Возврат завершает процедуры и функции во встроенном языке 1С 8.3

· 9 мин

Содержание

Оператор Возврат (англ. Return) немедленно завершает текущую процедуру или функцию и передаёт управление в точку вызова. В функции после него указывают значение: Возврат Сумма;. В процедуре пишут просто Возврат;. Всё, что стоит ниже в той же ветке кода, уже не выполняется.

Частое заблуждение новичков — считать, что возврат в 1С выходит только из цикла, как Прервать. Это не так: Возврат прекращает работу всей процедуры целиком, сколько бы циклов и условий ни было вложено. Поведение одинаково для всех конфигураций, потому что это механизм платформы, а не БП или ЗУП.

Синтаксис Возврат и где его можно писать

Оператор имеет две формы:

Возврат;              // в процедуре — просто выход
Возврат Выражение;    // в функции — выход с результатом

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

Функция ЦенаСНДС(Цена, СтавкаПроцент)
    Возврат Цена + Цена * СтавкаПроцент / 100;
КонецФункции

Вызов ЦенаСНДС(1000, 20) вернёт 1200.

Правила размещения:

  • 📌 Возврат пишут только внутри тела Процедура … КонецПроцедуры или Функция … КонецФункции.
  • 📌 В одной процедуре операторов может быть сколько угодно — по одному на каждую ветку выхода.
  • 📌 После Возврат ставится точка с запятой, как после любого оператора.
  • 📌 Регистр не важен: ВОЗВРАТ, Возврат и возврат для компилятора одно и то же.

В конфигураторе и EDT ключевое слово подсвечивается как зарезервированное. Если слово не окрасилось, проверьте раскладку: вместо кириллической «В» могла попасть латинская «B».

Чем возврат из процедуры отличается от возврата из функции

Разница в том, обязано ли что-то вернуться в точку вызова.

ПроцедураФункция
Форма оператораВозврат;Возврат Значение;
Вызов в выражении Х = Имя()недопустимосновной способ
Вызов отдельной строкой Имя();основной способдопустим, результат теряется
Если Возврат не встретилсяпроцедура просто дошла до концафункция вернула Неопределено

Функция без Возврат на каком-то пути

Если выполнение дошло до КонецФункции, не встретив Возврат, функция вернёт Неопределено. Ошибки при этом нет, и это коварно.

Функция СкидкаКлиента(Клиент)
    Если Клиент.Постоянный Тогда
        Возврат 5;
    КонецЕсли;
    // забыли вернуть значение для остальных клиентов
КонецФункции

Для обычного клиента вызов вернёт Неопределено. Выражение Сумма * СкидкаКлиента(Клиент) / 100 упадёт с ошибкой преобразования типа, причём не в этой функции, а там, где результат использовали. Ищут такое долго.

Правильный вариант — явный возврат на каждом пути:

Функция СкидкаКлиента(Клиент)
    Если Клиент.Постоянный Тогда
        Возврат 5;
    КонецЕсли;
    Возврат 0;
КонецФункции

Процедуру нельзя использовать как функцию

Строка Результат = ЗаполнитьТаблицу();, где ЗаполнитьТаблицу — процедура, приводит к ошибке «Использование процедуры как функции». Обратный случай допустим: функцию можно вызвать отдельной строкой, её результат просто отбросится.

Как вернуть несколько значений

Возврат отдаёт одно значение. Когда нужно несколько, собирают их в структуру:

Функция ИтогиПоДокументу(Документ)
    Итоги = Новый Структура("Сумма, НДС, Строк", 0, 0, 0);
    Для Каждого Строка Из Документ.Товары Цикл
        Итоги.Сумма = Итоги.Сумма + Строка.Сумма;
        Итоги.НДС   = Итоги.НДС + Строка.СуммаНДС;
        Итоги.Строк = Итоги.Строк + 1;
    КонецЦикла;
    Возврат Итоги;
КонецФункции

Вызывающий код читает Итоги.Сумма, Итоги.НДС и Итоги.Строк. Второй способ — менять параметры, переданные по ссылке (без Знач), но для читаемости структура лучше: по вызову сразу видно, что функция что-то возвращает.

Возврат внутри циклов и вложенных условий

В циклах у программиста три инструмента, и путать их нельзя.

ОператорЧто прерываетКуда переходит управление
Продолжитьтекущую итерациюна следующий проход того же цикла
Прерватьтолько ближайший циклна строку после КонецЦикла
Возвратвсю процедуру или функциюв точку вызова

Поиск с выходом из вложенных циклов

В 1С нет меток и нельзя выйти из двух циклов одним Прервать. Возврат решает задачу сразу: как только нашли нужное, выходим.

Функция НайтиЯчейку(Матрица, Искомое)
    Для НомерСтроки = 0 По Матрица.ВГраница() Цикл
        Строка = Матрица[НомерСтроки];
        Для НомерКолонки = 0 По Строка.ВГраница() Цикл
            Если Строка[НомерКолонки] = Искомое Тогда
                Возврат Новый Структура("Строка, Колонка", НомерСтроки, НомерКолонки);
            КонецЕсли;
        КонецЦикла;
    КонецЦикла;
    Возврат Неопределено;
КонецФункции

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

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

Ранний выход вместо глубокой вложенности

Приём называют «сторожевыми условиями» (guard clauses). Проверки, при которых делать нечего, ставят в начало и сразу выходят:

Процедура ПересчитатьСуммы(Объект)
    Если Объект.Товары.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;
    Если НЕ ЗначениеЗаполнено(Объект.Контрагент) Тогда
        Возврат;
    КонецЕсли;

    // основная логика без лишних уровней отступа
    Для Каждого Строка Из Объект.Товары Цикл
        Строка.Сумма = Строка.Цена * Строка.Количество;
    КонецЦикла;
КонецПроцедуры

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

Код после Возврат: недостижимые участки и как их найти

Всё, что стоит после Возврат в той же ветке, никогда не выполнится. Компилятор 1С такой код не запрещает и предупреждения не выдаёт.

Функция ОстатокТовара(Номенклатура)
    Результат = ПолучитьОстаток(Номенклатура);
    Возврат Результат;
    ЗаписатьЖурналРегистрации("Остатки", УровеньЖурналаРегистрации.Информация, , , "Запрошен остаток");
КонецФункции

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

Чем проверить модуль

  • 🔍 Синтаксический контроль конфигуратора (Ctrl+F7 в модуле) ловит грубые ошибки: Возврат со значением в процедуре или выход вне тела метода. Недостижимый код он не показывает.
  • 🔍 1С:EDT и подключённый к нему или к VS Code BSL Language Server подсвечивают недостижимые строки и функции, у которых есть путь без возврата значения.
  • 🔍 SonarQube с плагином для 1С показывает те же замечания по всей конфигурации — удобно для командной разработки.
  • 🔍 Отладчик: поставьте точку останова на подозрительную строку. Если она ни разу не срабатывает при заведомо проходящем сценарии, ищите выход выше.

Возврат «для отладки» — забытая ловушка

Разработчик временно ставит Возврат; в начало процедуры, чтобы отключить её, и забывает убрать. Обработка перестаёт что-либо делать, ошибок нет. Перед выкладкой изменений ищите по модулю строку Возврат; с комментарием вроде «временно» или «отладка» через Ctrl+F.

Возврат в блоке Попытка…Исключение и в транзакциях

Возврат можно писать и внутри Попытка, и внутри Исключение. Выход происходит сразу, как обычно. Но во встроенном языке 1С нет блока, который выполнится в любом случае (аналога finally). Значит, код после КонецПопытки при выходе из середины попытки пропускается.

Опасный пример с транзакцией

// ПЛОХО
НачатьТранзакцию();
Попытка
    Если НЕ ПроверитьОстатки(Документ) Тогда
        Возврат;   // транзакция осталась открытой
    КонецЕсли;
    ЗаписатьДвижения(Документ);
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение;
КонецПопытки;

При выходе по условию никто не вызвал ни ЗафиксироватьТранзакцию(), ни ОтменитьТранзакцию(). Транзакция висит до конца серверного вызова, а дальше платформа её откатит — и откатит заодно всё, что успели записать в той же транзакции выше по стеку. Внешний код получит ошибку уже в другом месте.

Не выходите оператором Возврат из открытой транзакции. Перед выходом закройте её явно:

// ХОРОШО
НачатьТранзакцию();
Попытка
    Если НЕ ПроверитьОстатки(Документ) Тогда
        ОтменитьТранзакцию();
        Возврат;
    КонецЕсли;
    ЗаписатьДвижения(Документ);
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение;
КонецПопытки;

Ещё лучше — вынести проверку до НачатьТранзакцию(), тогда открывать транзакцию вообще не придётся.

Возврат или ВызватьИсключение

СитуацияЧто использовать
Делать нечего, и это нормально (пустая табличная часть)Возврат;
Функция не нашла значение, и вызывающий это ожидаетВозврат Неопределено;
Операция невозможна, вызывающий должен узнать причинуВызватьИсключение "Текст причины";
Ошибку поймали, записали в журнал и хотим пробросить дальшеВызватьИсключение; в блоке Исключение

Если в блоке Исключение поставить Возврат и ничего не записать в журнал регистрации, ошибка исчезнет бесследно. Пользователь увидит, что «ничего не произошло», а причину не найдёт никто.

Возврат в обработчиках событий и серверных вызовах

Здесь несколько практических тонкостей, которые не видно из синтаксиса.

Возврат не отменяет запись документа

В обработчиках ПередЗаписью, ОбработкаПроведения, ОбработкаПроверкиЗаполнения выход через Возврат; всего лишь завершает обработчик. Запись или проведение продолжится. Чтобы остановить операцию, выставляют параметр Отказ:

Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    Если Товары.Количество() = 0 Тогда
        ОбщегоНазначения.СообщитьПользователю("Заполните табличную часть «Товары».");
        Отказ = Истина;
        Возврат;
    КонецЕсли;
    // остальная логика
КонецПроцедуры

ОбщегоНазначения.СообщитьПользователю есть в конфигурациях на БСП (БП 3.0, ЗУП 3.1, УТ 11). В самописной конфигурации без БСП используйте объект СообщениеПользователю.

Что можно вернуть с сервера на клиент

Функция с директивой &НаСервере, которую вызывают из &НаКлиенте, может вернуть только значения, передаваемые между клиентом и сервером:

  • ✅ примитивы: число, строка, дата, булево, Неопределено;
  • ✅ ссылки на объекты базы;
  • ✅ Структура, Массив, Соответствие, их фиксированные варианты;
  • ❌ ТаблицаЗначений, Запрос, РезультатЗапроса, объекты справочников и документов.

Если нужна таблица, на сервере её превращают в массив структур или помещают во временное хранилище (ПоместитьВоВременноеХранилище) и возвращают адрес.

Возврат в асинхронных функциях

В платформе 8.3 начиная с версии 8.3.18 есть Асинх Функция. Возврат Значение; внутри неё не отдаёт значение напрямую: вызывающий получает Обещание, а само значение — через Ждать.

&НаКлиенте
Асинх Функция ПодтвердитьУдаление()
    Ответ = Ждать ВопросАсинх("Удалить строку?", РежимДиалогаВопрос.ДаНет);
    Возврат Ответ = КодВозвратаДиалога.Да;
КонецФункции

&НаКлиенте
Асинх Процедура УдалитьСтроку(Команда)
    Если НЕ Ждать ПодтвердитьУдаление() Тогда
        Возврат;
    КонецЕсли;
    // удаление
КонецПроцедуры

Без Ждать условие Если НЕ ПодтвердитьУдаление() сравнит объект Обещание, а не булево, и логика сломается.

Ошибки с оператором возврата в 1С и правила чистого кода

Диагностика: выберите симптом

Правила, которые экономят время при сопровождении

  1. Один тип результата. Функция возвращает либо число, либо Неопределено как признак «не найдено». Не смешивайте числа, строки и булево из одной функции — вызывающему придётся проверять тип.
  2. Явный Возврат в последней строке функции. Даже если кажется, что все пути закрыты условиями выше.
  3. Ранние выходы — в начале метода. Возврат в середине длинного цикла на 80 строк читается плохо: его легко не заметить при доработке.
  4. Не больше одного уровня «хитрости». Если в функции пять Возврат в разных местах тела, проще разбить её на две-три функции поменьше.
  5. Вычисление — до Возврат, а не внутри. Возврат Результат; удобнее отлаживать, чем Возврат СложноеВыражение(...): значение видно в отладчике до выхода.
Можно ли выйти из модуля объекта целиком оператором Возврат?
Нет. Возврат завершает только текущую процедуру или функцию. Код раздела основной программы модуля выполняется один раз при создании объекта, и выход из модуля целиком не предусмотрен.
Выполнится ли код после КонецПопытки, если Возврат стоит внутри Попытка?
Нет. Блока, который выполняется при любом выходе, во встроенном языке 1С нет. Всё, что должно отработать обязательно, ставьте перед Возврат.
Влияет ли количество операторов Возврат на скорость кода?
Нет. Ранний выход, наоборот, ускоряет работу: не выполняются лишние проверки и проходы цикла.
Чем Возврат Неопределено отличается от отсутствия Возврат в функции?
Результат одинаковый — Неопределено. Но явный Возврат Неопределено показывает, что такой результат задуман, а не появился по ошибке.
Можно ли вернуть из функции ссылку и сразу обратиться к её реквизиту?
Да, например ПолучитьДоговор(Контрагент).Наименование. Но если функция может вернуть Неопределено, обращение к реквизиту даст ошибку. Сначала сохраните результат в переменную и проверьте через ЗначениеЗаполнено.